哈希算法对字符串的处理,有一个很强大的功能就是询问字符串的两个子串是否相同,特别适合于那种询问次数很多的情况下,可以避免超时。
来一个具体的题目
接下来我们来分析一下字符串哈希的概念
字符串哈希其实就是把字符转换成数字(也就是哈希值字符串有很多种字符,为此我们为区别不同字符我们赋予每个字符不同的值,这里我们直接借用字符的ASCII码来代表每个字符的值,同时我们最后的哈希值设为P进制,(经验值来看P一般取131,或者13331)接下来我们就可以求出前i个字符组成的子串的哈希值
char str[N];//用来存储字符串
P=131//代表这里哈希值是一个131进制的数
unsigned long long h[100010];//h数组设在main()函数外则默认初始为0
cin>>str+1;//从str+1开始读入
for(int i=1;i<=n;i++)//从i=1开始
h[i] = h[i - 1] * P +str[i];//h[i]代表字符串的前i个字符组成的字串的哈希值
但是我们知道字符串可能是很长,那么求出来的哈希值就会很大,所以这里我们用到了哈希的特点映射,把求出来的哈希值对2的64次方取模,这里有一个简便的方法就是用unsign long long数据类型来存储哈希值,因为该数据结构的取值范围为0~2的64次方,因此当溢出的时候就相当于对2的64次方取余的过程。
(映射取余,都会出现一个问题,那就是余数相同的冲突,为此字符串哈希不像之前的模拟散列表去解决这个冲突,而是去一些经验值来使得几乎没有可能性产生冲突,也就是P=131或13331,和对2的64次方就是这个经验值)对此有疑问的小伙伴可以去查找资料进一步了解。
有了前i个字符的字串的哈希值我们可以轻松得到任意连续字串的哈希值
ULL get(int l,int r){//这是一个求连续字串哈希值的函数
return h[r]-h[l-1]*p[r-l+1];//这里就要是求l~r区间的哈希值
}
最后我们给出完整的字符串哈希代码
#include <iostream>
using namespace std;
const int N = 100010, P = 131;//经验值P=131或者13331
typedef unsigned long long ULL;//unsigned long long的取值范围是0~2的64次方,同时是无符号,因此用ULL存储值就相当于对2的64方取余,利用了取值范围的溢出
char str[N];
ULL h[N],p[N];//p[N]数组用来打表避免重复计算P的多次方
ULL get(int l,int r){//这是一个求出任意字符串哈希值的函数
return h[r]-h[l-1]*p[r-l+1];//这里就要是求l~r区间的哈希值
}
int main(){
int n,m;
cin>>n>>m;
cin>>str+1;//从str+1开始读入
p[0]=1;
for(int i=1;i<=n;i++){//从i=1开始
p[i]=p[i-1] * P;
h[i] = h[i - 1] * P +str[i];//h[i]代表前i个字符组成的字符串的哈希值
}
while(m--){
int l1,r1,l2,r2;
cin>>l1>>r1>>l2>>r2;//读入两个字符串的左右界限
if(get(l1,r1)==get(l2,r2))puts("Yes");//对比两个字符串的哈希值是否相同
else puts("No");
}
return 0;
}
到头了,字符串哈希就讲完了