字符串哈希法是一个处理字符串的利器,可以简化很多字符串的问题。有时用这个方法比KMP更有优势。
什么是字符串前缀哈希?
举个“栗子”就明白!
例如 : str = “ABCDEFG”
H(1) = "A"的哈希值
H(2) = "AB"的哈希值
H(3) = "ABC"的哈希值
``` ```
步骤
1.把字符串看成是一个p进制的数,把字符串中的每个字符看成是p进制数中的每一位;
2.把p进制的数转化成10进制的数 mod Q;(不进行取模操作的话得到的数字可能比较大,存储会不方便)
3.这样就可以把任何一个字符串映射到从0~Q-1之间的一个数。
例如:H(4) = "ABCD"的哈希值 = (1* + 2* + 3* + 4* )mod Q;
注:取模通常取,如果我们用unsigned long long 来存储所有的h,就不需要取模运算了。溢出就相当于取模了,溢出就等价于模上
注:
①不能映射成0,通常映射成从1开始;
若H(1) = "A" = 0, 那么"AA"的哈希值也为0,可见会把不同的字符串映射为同一个数0,这样是不对的。
②字符串哈希假定RP足够好,不存在冲突。
应用
(求L到R的哈希值)
思路:
对于h[R]来说 R是低位,1是高位;对于h[L - 1]来说,L-1是低位,1是高位。
那么我们通过 h[L-1] * ,就可以使h[L-1]左移到与h[R]对齐。
得到公式: h[R] - h[L-1] *
这样,预处理每一个前缀哈希值之后,就可以用O(1)的时间复杂度算出任意一个子段的哈希值了。
预处理前缀哈希值:h[i] = h[i-1]*p + str[i]; (str[i]表示第i位上的字母)
典例——AcWing 841.字符串哈希
#include <iostream>
using namespace std;
typedef unsigned long long ull;
const int N = 100010,P = 131; //P代表P进制,根据经验,这里通常取P = 131或13331
char str[N];
ull h[N],p[N];
//h[]代表前缀哈希值,即前i个字符的哈希值
//p[]代表p的几次方
ull get(int l,int r)
{
return h[r] - h[l-1]*p[r-l+1];
}
int main()
{
int n,m;
scanf("%d%d%s",&n,&m,str + 1);
p[0] = 1;
for(int i = 1; i <= n; i ++ )
{
p[i] = p[i-1]*P;
h[i] = h[i-1]*P + str[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;
}