滚动哈希:O(n+m)时间内完成字符串匹配;
实现:选取两个合适的互素常数b和h(l<b<h),假设字符串C=c1c2c3...cm,定义哈希函数:H(C)=(c1*b^(m-1)+c2*b^(m-2)+...+cm*b^0)mod h
其中b是基数,相当于把字符串看作b进制数。这样,字符串S=s1s2s3...sn从位置k+1开始长度为m的字符串子串S[k+1...k+m]的哈希值,就可以利用从位置k开始的字符串子串S[k...k+m-1]的哈希值,直接进行如下计算:H(S[k+1...k+m])=(H(S[k...k+m-1])* b - sk*b^m + s(k+m)) mod h
于是,只要不断这样计算开始位置右移一位后的字符串子串的哈希值,就可以在O(n)时间内得到所有位置对应的哈希值,从而可以在O(n+m)时间内完成字符串匹配。在实现时,可以用64位无符号整数计算哈希值,并取h等于2^64,通过自然溢出省去求模运算。
代码:
typedef unsigned long long ull;
const ull b=100000007;//哈希的基数;
//a是否在b中出现
bool contain(string C,string S)
{
int m=C.length(),n=S.length();
if(m>n) return false;
//计算b的m次方
ull t=1;
for(int i=0;i<m;i++) t*=b;
//计算C和S长度为m的前缀对应的哈希值
ull Chash=0,Shash=0;
for(int i=0;i<m;i++) Chash=Chash*b+C[i];
for(int i=0;i<m;i++) Shash=Shash*b+S[i];
//对S不断右移一位,更新哈希值并判断
for(int i=0;i+m<=n;i++){
if(Chash==Shash) return true;//S从位置i开始长度为m的字符串子串等于C;
if(i+m<n) Shash=Shash*b-S[i]*t+S[i+m];
}
return false;
}