淘先锋技术网

首页 1 2 3 4 5 6 7

正文

字符串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;
}