当我们要判断两个字符是否相等时,如S1="abc",S2=“abb”。如果我们从头到尾逐个比较,则时间复杂度为O(m)。
这里我们就要用到字符串哈希来降到O(1)。
字符串哈希入门
说得通俗一点,字符串哈希实质上就是把每个不同的字符串转成不同的整数。
为什么会有这样的需要呢?很明显,存储一个超长的字符串和存储一个超大但是能存的下的整数,后者所占的空间会少的多,但主要还是为了方便判断一个字符串是否出现过,这是最基础的部分。
当然也很容易想到,如果有不同的字符串转成同一个整数,那么区分功能就基本废掉 ,所以我们需要一个算法把每个字符串转成唯一的整数。所以字符串哈希算法就应运而生,哈希算法的难点也就在于如何构造一个合适的Hash函数来满足我们的需求。
单哈希法
hash函数要做到一一对应
如果我们按这个转换规则,得到的hash_code值会很大很大,计算机根本存不下。这个时候就需要我们来取模了。 但取模的数不能乱取,不然会造成冲突(即两个字符串不相等,但取模后结果相同)。
这里我们用到一个小技巧,就是unsigned long long 如果溢出以后,它会自动帮你%2^64。
我们还可以从X(表示转换为X进制)来降低冲突。根据计算机科学家概率的方式来统计,发现X取:131,1331,13331… 它的冲突率是最小的。
冲突解决
代码块
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const ll mod=1e9+7;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int N=1000010;
const int X=13331;
char T[N];
char S[N];
ull h[N],x[N],hs[N];
vector<ull>v;
void BKDR_hash(char s[])
{
h[0]=s[0];
x[0]=1;
int ln=strlen(s);
for(int i=1;i<ln;i++)
{
h[i]=h[i-1]*X+s[i];
x[i]=x[i-1]*X;
}
}
ull get_hash(int left,int right)
{
if(!left)
return h[right];
else
return h[right]-h[left-1]*x[right-left+1];
}
int main()
{
ios::sync_with_stdio(false);
cin>>T;
BKDR_hash(T);
cin>>S;
ull hs2=0;
for(int i=0;i<strlen(S);i++)
hs2=hs2*X+S[i];
for(int i=0;i<strlen(T);i++)
cout<<get_hash(i,min(strlen(T)-1,i+strlen(S)-1))<<" ";
cout<<endl;
cout<<hs2<<endl;
//检测S串是否是T串的子串 通过对比hash_code值
}
运行截图