假设有字符串:ABCABCD
预处理前缀的哈希值
h[0] = ""的哈希值
h[1] = "A"的哈希值
h[2] = "AB"的哈希值
h[3] = "ABC"的哈希值
h[4] = "ABCA"的哈希值
…
哈希值计算方法,转化成P进制。
以26个大写字母为例,假设转化成26进制,那么"ABCA"的哈希值就是
(
1
∗
2
6
3
+
2
∗
2
6
2
+
3
∗
2
6
1
+
1
∗
2
6
0
)
m
o
d
Q
(1*26^3+2*26^2+3*26^1+1*26^0) mod Q
(1∗263+2∗262+3∗261+1∗260)modQ
注意不能映射成0,例如A是0,那么A…A也是0,无法区分。
当P=131或13331,Q=2^64时,字符串哈希基本不会冲突。
可以用unsigned long long存储,这样相当于自动模Q
这里不考虑冲突时的情况。
有了预处理的哈希值,可以得到字符串任意子段的哈希值,不妨设子段是[L, R]
则子段的哈希值是
h
[
R
]
−
h
[
L
−
1
]
∗
P
R
−
L
+
1
h[R] - h[L-1]*P^{R-L+1}
h[R]−h[L−1]∗PR−L+1
其中
h
[
L
−
1
]
∗
P
R
−
L
+
1
h[L-1]*P^{R-L+1}
h[L−1]∗PR−L+1是为了使
h
[
L
−
1
]
h[L-1]
h[L−1]对齐
h
[
R
]
h[R]
h[R]的前面L-1位。
#include <iostream>
using namespace std;
typedef unsigned long long ULL;
const int N = 100005, P = 131;
ULL h[N], p[N];
int n, m;
char str[N];
int main() {
scanf("%d%d%s", &n, &m, str);
p[0] = 1;
for (int i = 1; i <= n; i ++ ) {
p[i] = p[i - 1] * P;
h[i] = h[i - 1] * P + str[i - 1];
}
int l1, r1, l2, r2;
while (m -- ) {
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
ULL t1 = h[r1] - h[l1 - 1] * p[r1 - l1 + 1];
ULL t2 = h[r2] - h[l2 - 1] * p[r2 - l2 + 1];
if (t1 == t2) printf("Yes\n");
else printf("No\n");
}
return 0;
}