一.hash的引入.
考虑一个问题,给定n( )个范围在 内的数,要求快速查询x是否在这n个数中.
直接桶就MLE了,但是没关系我们可以直接上平衡树,或者直接STL里的set或者map就好了,甚至你可以给n个数排个序再二分查找…
但是如果要求
查询上述算法就都没了…这个时候hash的优势就体现出来了.
二.hash表.
hash表是啥?其实也是个桶,好吧其实桶就是一种最简单的hash表.
我们考虑桶个实现原理,桶其实就是开了一个数组 表示 是否存在.然后我们思考对于上面这个问题,发现开 个桶也太浪费了,平均要 个位置才有一个数.
所以我们考虑让设一个函数 ,考虑让 存x,然后让 的值域变得比较小,就可以省下很多内存了.
现在考虑如何设置 ,最简单的当然是让 ,其中M是任意一个常数.不过事实证明当M为一个质数时就很少会出现 冲突的情况了,所以M一般取一个n的4~5倍的大质数.
但是冲突出现得再少照样会有,所以我们考虑如何处理冲突,一般有两种方案:
1.给每一个位置存一个链表,表示
为当前位置的所有数.
2.当插入
冲突时,就往
的位置放(其中k是一个比较小的常数,事实证明k取3,5,7会比较优秀),如果还是冲突就尝试
放…直到没有冲突.
一般情况下本人比较喜欢用第2种(第2中只要写个循环就好了,第1种要写链表诶),所以我们这里只提供第2种写法的插入与查询:
struct Hash_table{
int h[M+9],v[M+9]; //v[x]是存在第x个位置的数的真实值
void add(int &a,const int &b){a+=b;if (a>=M) a-=M;}
void insert(int x){ //插入一个数
int t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
++h[x];v[x]=t;
}
bool find(int x){ //查找一个数
int t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
return h[x]?1:0;
}
}
三.字符串hash.
我们考虑一些复杂的信息,例如字符串上的一些信息给如何运用hash表来处理.
很容易想到我们可以把字符串看成一个P进制的数,第一个字符是最高位,第二个字符是第二高位…这样每个字符串我们都可以看成一个数字了,接下来我们设一个字符串s对应的P进制数为 .
但是这个数字太大怎么办?我们可以直接讲这个数字对一个数取模,这个数通常可以取 ,这样就可以直接用unsigned long long来存减少取模运算了,而且这样子通常不会被卡.在这个情况下可以让P等于131或者13331,事实证明这两个数产生冲突的情况较小.
再来考虑在这样的字符串hash的处理下,对于两个串
和
,如果我们知道
和
对应的P进制数
与
,那么t对应的P进制数就可以这样求:
那么我们要求一个串 的一个子串 对应的P进制数就可以通过预处理前缀和来搞了.
求一个串s[l…r]对应的P进制数就可以这样写:
ULL Hash(ULL *a,int l,int r){return a[r]-a[l-1]*Pow[r-l+1];} //其中a数组是字符串s对应的前缀和
四.例题与代码.
这里就只给一道字符串hash的例题.
题目:BZOJ4779.
这道题只需要二分一下可行的长度len就可以用字符串hash过啦(一开始我还天真的认为 能过…).
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
typedef unsigned long long ULL;
char rc(){
char c=getchar();
while (c<'A'||c>'Z') c=getchar();
return c;
}
const int N=500;
const ULL P=131,M=5000081;
ULL Pow[N+9];
ULL Hash(ULL *a,int l,int r){return a[r]-a[l-1]*Pow[r-l+1];}
struct Hash_table{
int h[M+9];
ULL v[M+9];
void add(ULL &a,const ULL &b){a+=b;if (a>=M) a-=M;}
bool find(ULL x){
ULL t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
return h[x]?1:0;
}
void insert(ULL x){
ULL t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
++h[x];v[x]=t;
}
void erase(ULL x){
ULL t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
--h[x];
if (h[x]<0) h[x]=0;
}
}h;
int n,m,ans;
ULL s1[N+9][N+9],s2[N+9][N+9];
bool check(int len){
int r,flag;
for (int l=1;l+len-1<=m;++l){
r=l+len-1;flag=0;
for (int i=1;i<=n;++i)
h.insert(Hash(s1[i],l,r));
for (int i=1;i<=n;++i)
if (h.find(Hash(s2[i],l,r))) flag=1;
for (int i=1;i<=n;++i)
h.erase(Hash(s1[i],l,r));
if (!flag) return true;
}
return false;
}
Abigail into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
s1[i][j]=s1[i][j-1]*P+rc();
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
s2[i][j]=s2[i][j-1]*P+rc();
}
Abigail work(){
Pow[0]=1;
for (int i=1;i<=m;++i)
Pow[i]=Pow[i-1]*P;
int l=1,r=m;
for (int mid=l+r>>1;l+1<r;mid=l+r>>1)
check(mid)?r=mid:l=mid;
ans=check(l)?l:r;
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}