1.字符串Hash概念
简单来说字符串Hash就是将一个字符串转化成一个整数,并保证字符串不同,得到的Hash值不同,这样就可以用来判断一个该字串是否重复出现过。
2.Hash方法
给定一个字符串s[ n ]=s[0]s[1]s[0]…s[n-1],
对于字母是s[ i ],我们规定 idx[ s[i] ]=x-‘a’+1。(当然也可以直接用s[ i ] 的ASCII值)
1,自然溢出法
unsigned long long Hash[n],
hash[ i ]=hash[i−1]∗p+idx(s[i])
2,单Hash法
hash[i]=( (hash[i−1])∗p+idx(s[i]) ) % mod
其中p和mod均为质数,且有p<mod。
对于此种Hash方法,将p和mod尽量取大即可,这种情况下,冲突的概率是很低的。
3.求子串的Hash值
一个字符串 s1s2s3s4的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
现在我们想求s3s4的hash值,不难得出为s2*p+s3
故可以归纳出以下结论
hash = hash[ r] - hash [l - 1]p^(r- l+ 1).
如果需要反复对子串求解hash值,预处理p的n次方效果更佳。
4.例题
很久很久以前,森林里住着一群兔子。
有一天,兔子们想要研究自己的 DNA 序列。
我们首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英文字母)。
然后我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。
注意两个兔子一模一样只可能是他们的 DNA 序列一模一样。
输入格式
第一行输入一个 DNA 字符串 S。
第二行一个数字 m,表示 m 次询问。
接下来 m 行,每行四个数字 l1,r1,l2,r2,分别表示此次询问的两个区间,注意字符串的位置从1开始编号。
输出格式
对于每次询问,输出一行表示结果。
如果两只兔子完全相同输出 Yes,否则输出 No(注意大小写)。
数据范围
1≤length(S),m≤1000000
输入样例:
aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2
输出样例:
Yes
No
Yes
代码实现
#include<stdio.h>
#include<string.h>
int hash[1000010],p=133,pi[1000010];
unsigned long long hashi(int l,int r)//获取子串hash值的函数
{
return (hash[r]-hash[l-1]*pi[r-l+1]);
}
int main()
{
char a[1000010];
while(scanf("%s",a)!=EOF)
{
int i,len;
len=strlen(a)+1;
for(i=len; i>=0; i--)
a[i]=a[i-1];//hash值从1开始,故将字符串向后移一位
for(i=1,hash[0]=0,pi[0]=1; i<=len; i++)
{
hash[i]=hash[i-1]*p+a[i]-'a'+1;//计算hash值
pi[i]=pi[i-1]*p;//将p的几次方以数组形式预处理
}
int m,l1,r1,l2,r2;
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(hashi(l1,r1)==hashi(l2,r2))//判断子串hash值是否相等
printf("Yes\n");
else
printf("No\n");
}
}
return 0;
}