字符串Hash
就是通过某种方法把一个字符串转换为一个整数,比较时就不用一个一个去比较,但要尽量降低不同字符串这个整数的重合率。
主要应用
寻找长度为n的主串S中的匹配串T(长度为m)出现的位置或次数的问题属于字符串匹配问题,也可以用KMP算法。如果是从主串中每次选出两个子串判断是否匹配问题,还是要用字符串hash求解。
方法
给定一个字符串S=s1s2s3…sn,对于字母x,我们规定idx(x)=x-‘a’+1。(我们也可以直接用si的ASCII值)
公式
hash[i]=hash[i-1]*p+id(s[i])%mod
相当于对2的64次方-1取模(即使用unsigned long long存储可能也会溢出,要取模,还要降低重合率)
p和mod均为质数,且p<mod。
求子串的Hash值
hash[1]=s1
hash[2]=s1 * p+s2
hash[3]=s1 * p ^ 2+s2 * p+s3
hash[4]=s1 * p ^ 3+s2 * p ^ 2+s3 * p+s4
结论:hash=hash[y]-hash[x-1] * p^(y-x+1)(不放心还可以继续取模,通常p取131或1331)
例题:
https://www.acwing.com/problem/content/description/140/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
unsigned long long h[1000010],p[1000010]; //数组过大放函数外面
char a[1000010];
unsigned long long hash(int x,int y)//递归
{
unsigned long long s=h[y]-h[x-1]*p[y-x+1];//子串hash公式
return s;
}
int main()
{
while(~scanf("%s",a))
{
h[0]=0,p[0]=1;
int n=strlen(a)+1;
int i;
for(i=n; i>=0; i--)
a[i]=a[i-1];
for(i=1; i<=n; i++)
{
h[i]=h[i-1]*131+a[i]-'a'+1;
p[i]=p[i-1]*131;
}
int t;
scanf("%d",&t);
while(t--)
{
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
if(hash(x1,y1)==hash(x2,y2))
printf("Yes\n");
else
printf("No\n");
}
}
return 0;
}