解决问题:
字符串hash主要应用在:在长度为n的主串S中匹配长度为m的匹配串T,返回起始位置。
Hash的目的:
通过字符串Hash函数把一个任意长度的字符串映射成一个非负整数,并且其冲突概率为零。
具体方法:
取一固定值P,把字符串看作P进制数,并分配一个大于0的数值,代表每种字符。一般来说,我们分配的数值都远小于P。取一固定值M,求出P进制数对M的余数,作为该字符串的Hash值。
一般来说,我们取P = 131 或 13331,此时Hash值产生冲突的概率极低,只要Hash值相同,我们就可以认为原字符串是相等的。通常我们取M = 2 ^ 64,即直接使用unsigned long long 类型储存这个Hash值,在计算时不处理算数溢出问题,产生的溢出相当于自动对 2 ^ 64取模。
具体运算:
记字符串S的Hash值为H(S),那么在S后添加一个字符c构成的新字符串的 S + c 的Hash值H(S + c) = (H(S) * p + val[c]) mod M。val[c]为字符c的代表数值。
同理去除字符串首位(最高位)H[S] = H[c + S] - val[c] * p ^ length(S);
不足之处:
对于Hash值在0 ~ n之间均匀分布的Hash函数,出现不同字符串Hash值相等的期望步骤是O(sqrt(n)),在可以当作选取Hash函数的参考。
同时为了及降低出现相同Hash值的概率,可以采取双哈希来降低出现相同Hash值的概率,只有两个Hash值相等才认定字符串匹配。如取:10 ^ 9 + 7 与 10 ^ 9 + 9。
代码举例:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ULL;
ULL base = 131;
int main(){
string s;
cin>>s;
ULL res = 0;
for(int i = 0; i < s.length(); i++){
res = res * base + s[i] - 'a' + 1;
}
cout<<res;
}