感觉没找到什么例题,但也是一个重要的思考方式,复杂度O(1)。
经典问题:如果有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它。
字符串hash是指将一个字符串s映射为一个,使得该整数可以尽可能唯一的代表也就是唯一标识。换言之,如果两个字符的hash值相同那么我们可以认为两者相同。
字符串hasn函数有很多种,下面这篇博客有做比较:
各种字符串Hash函数比较
总而言之,反而简单的BKDRHash无论是在实际效果还是编码实现中,效果都是最突出的,一般也只讲这种。
首先我们定一个 素数 seed (常见 31,131,313,3131,3131313 等等) ,则 hash值就等于 每一个字符的Ascll码乘以seed的n次方 累加 之后再对哈希数组大小取余得到余数就是hash值,n就是位数,size 就是hash数组的大小。
贴一段打比赛用的到的代码:
ull BKDRHash(char *s){
ull seed=131,key=0;
while(*s) key=key*seed+(*s++);
return key%mod;
}
公式一:hash[i] = ( hash[i-1]*p + idx(s[i]) )%mod
基于字符串hash函数,可以求字符串的任何一个子串的hash值:
公式二:hash[l…r] = ( ( hash[r] - hash[l-1] * p^(r-l+1) ) %mod + mod ) % mod。
typedef unsigned long long ull;
const int maxn=1e6+7;
const int seed=131;
char str[maxn];
ull h[maxn],p[maxn];//h数组是求前缀哈希值,p数组是求进制的i次幂
//求一段区间的哈希值
ull get(int l,int r){
return h[r]-h[l-1]*p[r-l+1]; }
int main(){
scanf("%s",str+1);
int n=strlen(str+1);
p[0]=1;
for(int i=1;i<=n;i++){
h[i]=h[i-1]*seed+str[i]-'a'+1;
//注意:“+1”不能省,否则“agh”与“gh”表示用一个数,也可以写h[i]=h[i-1]*seed+str[i];
p[i]=p[i-1]*seed;
}
}
注意点:
1、一般数据在1e6的,就不要用mod了,能用ull就用ull,可能有误差。
2、多个字符串,p[maxn]可以预处理。
3、字符串中输出一些字符,用后一个区间哈希值+前一个区间哈希值*后一个区间长度(l-r+1)即可。