正文
字符串hash顾名思义就是对一个字符串给一个专属的hash值给它。也就是对于两个相同的字符串他们的hash值一样,不相同的反之。
具体操作就是 hash[i]=hash[i-1]*seed+ch
解释起来就是一个字符串1到i位字符组成的字符串值为1到i-1组成的字符串的hash值乘种子再加上当前字母的值。
对于一个字符串判断第1-i位和第j-(j+i-1)位子串是否相等可以这样做:
if (hash[i]==hash[j+i-1]-hash[j-1] *ksm[i])那么相等,反之不相等。
解释一下,ksm[i]表示种子数seed^i的值,这里乘ksm是因为hash[j-1]的值推到hash[j+i-1]时乘了i次seed,这时我们减回去就是第j-(j+i-1)位子串的hash值
既然是hash那么我们希望重复概率较小,这点可以通过更改种子数和模数(推荐一个较好的模数402653189)优化。
字符串hash多用于匹配字符串,在一定程度上也可以发挥kmp的作用。
例题
noip2020字符串匹配
题目描述
简略的解析
枚举AB的长度然后用字符串hash判重,再开个桶累计方案数
代码(有些丑)
#include<cstdio>
#define R register
using namespace std;
const long long seed=131;
int sum[1050005],sum2[1050005];
unsigned long long hash[1050005],ksm[1050005];
long long t;
char s[1050005];
int main()
{
freopen("string.in","r",stdin); freopen("string.out","w",stdout);
scanf("%d",&t);
ksm[0]=1;
for (R long long i=1;i<=(1<<20);++i) ksm[i]=ksm[i-1]*seed;
while (t>0)
{
--t;
char ch=getchar();
int len=0;
while (ch<'a'||ch>'z') ch=getchar();
while (ch>='a'&&ch<='z') s[++len]=ch,hash[len]=((hash[len-1]*seed)+(s[len]-'a'+1)),ch=getchar();
int wss[27]={},dis[27]={},jls[27]={};
wss[s[len]-'a'+1]=1; sum[len]=1;
for (R long long i=len-1;i>=1;--i)
{
sum[i]=sum[i+1],++wss[s[i]-'a'+1];
if (wss[s[i]-'a'+1]%2==1) ++sum[i];
else --sum[i];
}
dis[s[1]-'a'+1]=1; sum2[1]=1;
long long ans=0;
for (R long long i=2;i<=len-1;++i)
{
for (R long long j=sum2[i-1];j<=26;++j) ++jls[j];
ans+=jls[sum[i+1]];
for (R int head=i+1;head+i-1<=len-1;head+=i)
{
if ((hash[head+i-1]-hash[head-1]*ksm[i])!=hash[i]) break;
ans+=jls[sum[head+i]];
}
sum2[i]=sum2[i-1];
++dis[s[i]-'a'+1];
if (dis[s[i]-'a'+1]&1) ++sum2[i];
else --sum2[i];
}
printf("%lld\n",ans);
}
fclose(stdin);fclose(stdout);
return 0;
}