字符串哈希可以帮助我们处理很多与字符串有关的操作,比如快速的判断某个字符串是否存在,或者某两段字符串是否相同,还有著名的字符串匹配问题都可以使用字符串哈希来处理
既然字符串哈希这么神奇,那快来学习它吧!
字符串哈希就是给每个字符串一个哈希值,每个字符串对应的哈希值都是它独一无二的标签(一般情况下)
比如:
h[“aaa”] = 123;
h[“bbb”] = 189;
h[“efwqe”] =3442;
类比10进制,我们规定字符串为p进制:
而经过进制计算得到的值再取模一个数就作为该字符串的哈希值
根据经验值,我们取进制p为131或13331 , 取模的数一般取2^64
这里关于取模的问题就有一个小技巧了,因为unsigned的取值上界刚好到2^64,因此直接将存储计算的哈希值变量定义为unsigned就行啦!
那么给你一段字符串,比如:abcdefghijk
你可以很容易计算出从第一个位置到每个位置的字符串的哈希值
//这里字符串从下标为1的地方开始存便于操作
for(int i=1;s[i];i++){
h[i]=h[i-1]*P+s[i];
}
其实,在这个计算过程中可以将P次方的值存储好,以便接下来的操作
for(int i=1;s[i];i++){
p[i]=p[i-1]*P;
h[i]=h[i-1]*P+s[i]-'0';
}
已经计算好从第一个位置到每一个位置的字符串哈希值了,那么怎么计算任意两个位置间字符串的哈希值呢
比如要计算该字符串中"def"的哈希值,也就是4~6的字符串
我们可以这样计算:
先将h[“abc”]的值乘P^3,也就是让abc向高位移动三位,与"abcdef"的哈希值高位对齐,然后相减
就将"abcdef"中的高位"abc"消去啦,得到了"def"的哈希值
可知,算任意两个位置l~r间的字符串哈希值,可以这样算:
unsigned val = h[r] - h[l - 1]*p[r - l + 1]
这里就用到了之前储存好的P次方的值了,p[ k ]就是P的k次方,乘以P的k次方就是向高位移动k位
利用这个就可以去判断两个字符串是否相同,可以处理字符串匹配等问题啦!
下面附上一道acwing的例题:
给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1] 和 [l2,r2] 这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数 n 和 m,表示字符串长度和询问次数。
第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。
接下来 m 行,每行包含四个整数 l1,r1,l2,r2,表示一次询问所涉及的两个区间。
注意,字符串的位置从 1 开始编号。
输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1≤n,m≤105
输入样例:
8 3
aabbaabb
1 3 5 7
1 3 6 8
1 2 1 2
输出样例:
Yes
No
Yes
代码:
#include<iostream>
#include<string>
using namespace std;
const int N=100010,P=131;
unsigned long long p[N],h[N];
int main()
{
int n,m;
cin>>n>>m;
string s,s1;
cin>>s1;
s=" ";
s+=s1;
p[0]=1;
for(int i=1;s[i];i++)
{
p[i]=p[i-1]*P;
h[i]=h[i-1]*P+s[i];
}
while(m--)
{
int l1,l2,r1,r2;
unsigned s1,s2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
s1=h[r1]-h[l1-1]*p[r1-l1+1];
s2=h[r2]-h[l2-1]*p[r2-l2+1];
if(s1==s2) puts("Yes");
else puts("No");
}
return 0;
}
学习算法,永无止境,一起加油!!!