字符串哈希
1、思路
全称字符串前缀哈希法,把字符串变成一个p进制数字(哈希值),实现不同的字符串映射到不同的数字。
对形如 X1X2X3⋯Xn−1Xn 的字符串,采用字符的ascii 码乘上 P 的次方来计算哈希值。
映射公式 (X1×Pn−1+X2×Pn−2+⋯+Xn−1×P1+Xn×P0)%Q
映射公式 (X1×Pn−1+X2×Pn−2+⋯+Xn−1×P1+Xn×P0)%Q
注意点:
1. 任意字符不可以映射成0,否则会出现不同的字符串都映射成0的情况,比如A,AA,AAA皆为0
2. 冲突问题:通过巧妙设置P (131 或 13331) ,Q为264的值,一般可以理解为不产生冲突。
问题是比较不同区间的子串是否相同,就转化为对应的哈希值是否相同。
求一个字符串的哈希值就相当于求前缀和,求一个字符串的子串哈希值就相当于求部分和。
前缀和公式 h[i+1]=h[i]×P+s[i]h[i+1]=h[i]×P+s[i] i∈[0,n−1]i∈[0,n−1] h为前缀和数组,s为字符串数组
区间和公式 h[l,r]=h[r]−h[l−1]×Pr−l+1h[l,r]=h[r]−h[l−1]×Pr−l+1
区间和公式的理解: ABCDE 与 ABC 的前三个字符值是一样,只差两位,
乘上 P2P2 把 ABC 变为 ABC00,再用 ABCDE - ABC00 得到 DE 的哈希值。
2、代码模板
#include<iostream> using namespace std; typedef unsigned long long int ULL;//因为要求p的64次方 const int N=1e5+10; const int P=131;//或者13331 ULL h[N],p[N];//h存哈希值,即前缀和,p存预处理的P的次方 int n,m; char str[N];//存字符串,将字符串当成p进制的数 ULL get(int l,int r) { return h[r]-h[l-1]*p[r-l+1];//返回子串的p进制数 } int main() { scanf("%d%d%s",&n,&m,str+1);// p[0]=1;//P的0次方为1 for(int i=1;i<=n;i++)//从第一个字母开始遍历 { p[i]=p[i-1]*P;//预处理p的次方 h[i]=h[i-1]*P+str[i];//存每个前i个p进制的值,将之前的左移一位,再加上最后的一位的值 } while(m--) { int l1,l2,r1,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; }
3、题目