该算法的目的是将字符串转化为哈希值,这样的话在做字符串匹配的题的时候可以大大降低时间复杂度,仅需O(1)的时间复杂度即可。
首先要将字符串看成一个p进制的数,一般可以看作131或者13331。一般我们需要两个数组,p[N]用来存放这是131的第几次方,p[0]=1,h[N]用来存放字符串的哈希值。在存放哈希值时,需要将字符转化为数字。
当字符串都为小写字母时:
例如:字符串s为abcd,存放的分别是h[a],h[ab],h[abc],h[abcd]的哈希值。倘若我们知道h[abc]的哈希值,我们如何去求h[abcd]的哈希值呢?
求abcd的哈希值,也就相当于abc向高位进了一位,同时加上了字符d的值,d的值用1-26来表示为4。所以我们可以推出h[i]=h[i-1]*131+s[i](1-26的数),所以转换为h[i]=h[i-1]*131+s[i]-'a'+1。
int n=strlen(s+1); //我们要使字符串的下标从1开始,所以输入的时候s+1
for(int i=1;i<=n;i++)
{
h[i]=h[i-1]*base+s[i]-'a'+1; //将字符a-z转换为1-26,所以减一之后要加一
p[i]=p[i-1]*base; //这里的base为131
}
当我们要求一段区间内的哈希值时,又该如何去求呢?
我们需要求出[L,R]区间内的哈希值:h[L-R]=h[R]-h[L-1]*131^(R-L+1),其中L-1是从最低为到达现在的位置,一共要进(R-L+1)次方,举例说明一下:字符串abcdef,依次求出h[a],h[ab],h[abc],h[abcd],h[abcde],h[abcdef],那么b从一开始ab中的位置,到达abcdef中的b的位置,一共进了4个进制,也就是(R-L+1)进制,也就相当于b从一开始的个位到达了现在的万位,所以p数组要记录每一次的进制结果。
ULL get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1]; //每次乘上131的(r-l+1)次方,(r-l+1)用p数组表示
}
那么对于区间内的哈希值我们已经知道如何去求了,那么又该问求出哈希值了又有什么用呢?
看一道例题就明白了链接放在这里了:138. 兔子与兔子 - AcWing题库
大致题意就是要我们判断[l1,r1],[l2,r2]区间内的字符串是否相等,那么我们已经分别知道了这两个区间的哈希值,那么我们直接判断他们的哈希值是否相等不就好了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=10000000,base =131;
typedef unsigned long long ULL;
ULL h[N];
ULL p[N];
char s[N];
ULL get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1]; //每次乘上131的(r-l+1)次方,(r-l+1)用p数组表示
}
int main()
{
scanf("%s",s+1);
p[0]=1;
int n=strlen(s+1);//提前算出来,不能写到for循环内,不然每次算一下就要超时
for(int i=1;i<=n;i++)
{
h[i]=h[i-1]*base+s[i]-'a'+1; //将字符a-z转换为1-26,所以减一之后要加一
p[i]=p[i-1]*base;
}
int m;
scanf("%d",&m);
while(m--)
{
int l1,r1,l2,r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(get(l1,r1)==get(l2,r2)) printf("Yes\n");
else printf("No\n");
}
return 0;
}