这种字符串哈希方法的基本想法就是把字符串转换成n进制数存起来。以纯英文字母字符串(例如abcdefghijk)为例,可以转换为27进制或更高进制数(31进制或131进制),因为aa与a不同,不能用00和0来区分,所以最少要27进制。
而因为这种很高进制的数可以变得很大,所以我们用unsigned long long 来储存它们。如果所求的值超出了unsigned long long的范围也就是大于2^64-1,就会自动对2^64-1取模。又因为2^64-1实在是太大了,所以除非你人品太差,否则不会出现冲突的。。。
用上述的哈希方法来实现字符串匹配,即判断一个串是否是另一个串的子串。例如大串a和小串b,进制数为base。用一个H[i]存a[a的长度]到a[i]这个子串的哈希值(之所以从后往前存是为了后来匹配的时候从前往后匹配方便,放过来从前往后存其实一样)。用xp[i]来存base的i次方。这样,对任意以i为起点B长度的a的字串,它的哈希值可以表示为 H[i]-H[i+B]*xp[B] 将这个值与b串的哈希值比较看看是不是一样就可以了。 实际上把a串遍历一遍,b串遍历一遍,对比的时候遍历a-b长度次,可以看作是o(n+m)的算法。以下是纯英文字母字符串(只有大写或小写)代码,其余字符串可以改变base值来实现。
#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
const int maxn=1000005;
const int base=131;
ll H[maxn],xp[maxn];
int main()
{
int i;
xp[0]=1;
for(i=1;i<maxn;++i){
xp[i]=xp[i-1]*base;
}
string a,b;
cin>>a;//输入a串
int A=a.size();
H[A]=0;
H[A-1]=a[A-1]-'a'+1;
for(i=A-2;i>=0;--i){
H[i]=H[i+1]*base+a[i]-'a'+1;
}
while(cin>>b){
int B=b.size();
ll b_num=0;
for(i=B-1;i>=0;--i){
b_num=b_num*base+b[i]-'a'+1;
}
bool flag=1;
for(i=0;i<A-B+1;++i){
ll a_num=H[i]-H[i+B]*xp[B];
if(a_num==b_num){
flag=0;
break;
}
}
if(flag) cout<<"No\n";
else cout<<"Yes\n";
}
return 0;
}