散列(hash)表
一、什么是hash表
hash表是一种搜索算法,可以根据hash函数和各元素的值来确定存储的位置,将位置保存在hash表中,以实现快速存储和查找。
但是hash函数一般不可能完美,会产生多个值对应同一位置的情况,这就称作hash冲突。
所以说hash表最重要的地方就是如何写hash函数能使发生hash冲突的可能性小和如何处理hash冲突。
二、处理hash冲突的方法
这里以acwing的例题 :acwing840为例
https://www.acwing.com/problem/content/842/

1.开放寻址法
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+3,INF=0x3f3f3f3f;//数组要开两倍防止没坑用,并且要是质数,这样hash冲突的情况最少
int h[N];
int find(int x)//查询
{int t=(x%N+N)%N;/*哈希函数:用%N的余数来确定该元素的位置(x+N)%N不行,因为x的值远大于N的值,如果x为负数那么N+x也仍为负数*/while(h[t]!=INF&&h[t]!=x)//这个坑有人并且不是该元素继续{t++;//继续往下找坑if(t==N)t=0;//坑从中间找遍了,从头开始找}return t;//找到位置了返回位置
}
int main()
{memset(h,0x3f,sizeof h);int n;cin>>n;while(n--){char op[2];int x;scanf("%s%d",op,&x);if(*op=='I'){h[find(x)]=x;//找坑的位置,安排上x}else{if(h[find(x)]==INF)puts("No");else puts("Yes");}}return 0;
}
2.拉链法
//拉链法
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+3;
int h[N],e[N],ne[N],idx;//分别表示hash函数的位置且为链表的头节点,拉链上点的值,拉链上点指向的下一位置,当前处理的位置
void insert(int x)//插入
{int k=(x%N+N)%N;e[idx]=x;//当前点的值ne[idx]=h[k];//当前点的下一位指向原hash位置头节点所指向的位置h[k]=idx++;//原hash位置头结点指向当前位置并且当前位置用过了,++
}
bool find(int x)//查询
{int k=(x%N+N)%N;for(int i=h[k];i!=-1;i=ne[i])//找hash位置头结点的拉链{if(e[i]==x)//找到{return true;}}return false;//找不到
}
int main()
{memset(h,-1,sizeof h);//初始化hash表中头结点指向-1int n;cin>>n;while(n--){char op[2];int x;scanf("%s%d",op,&x);if(*op=='I'){insert(x);}else{if(find(x))puts("Yes");else puts("No");}}return 0;
}
三、字符串hash
听y总说用来水一些特别难的字符串题,可以利用前缀hash求出字符串中任意子串的hash值。
思路:
将某一字符串s转换成一个p进制的数字,例如131进制数字。这个值看作是字符串的hash值,唯一的代表该字符串s。
字符串均由p进制中(0~p-1)个数字构成,相同的字符对应的p进制数字相同。
这里p可以取131或者是13331,这是经验值,可以把字符串映射成0~Q-1之间的数(Q=2^64)。
注:①字母不能映射成0 否则容易冲突
②用这个方法默认人品足够好,不考虑冲突,所以要用经验值。
例题:acwing 841. 字符串哈希
https://www.acwing.com/problem/content/843/

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=100010,P=131;
int n,m;
char str[N];
ULL h[N],p[N];//p用来存储p的多少次方
ULL get(int l,int r)
{return h[r]-h[l-1]*p[r-l+1];//前缀和求解
}
int main()
{scanf("%d%d%s",&n,&m,str+1);p[0]=1;for(int i=1;i<=n;i++)//预处理出所有前缀的hash值,{h[i]=h[i-1]*P+str[i];//*P表示这里用了移位(可参考位运算)p[i]=p[i-1]*P;}while(m--){int l1,l2,r1,r2;scanf("%d%d%d%d",&l1,&r1,&l2,&r2);if(get(l1,r1)==get(l2,r2))puts("Yes");else puts("No");}return 0;
}
