BKDR_Hash
字符串的哈希值采取前缀和方式计算
选择数字P = 31作为优质乘子
str: | NULL | a | b | c | d | e | f |
---|---|---|---|---|---|---|---|
hash | 0 | ||||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
hash[0] = 0;
hash[1] = hash[0] * P + str[1];
hash[2] = hash[1] * P + str[2];
hash[3] = hash[2] * P + str[3];
…………………………
hash[6] = hash[5] * P + str[6];
最终hash[6]存放的就是整个字符串的哈希值。
在计算哈希值的过程中可能会爆数据,这时就要采取措施保证数据正确性!选择unsigned long long类型可以自动对 2 64 2^{64} 264取模。(最大存储数值为 2 64 − 1 2^{64}-1 264−1,也就是全1状态,加1之后全清0)
无符号整数较小值减较大值问题?
那么无符号整型 2 − 4294967295 2-4294967295 2−4294967295是否等于3呢?
− 4294967295 -4294967295 −4294967295等于 − 1111...1111 -1111...1111 −1111...1111从最后一个1的前一位开始取反得到 0000...0001 0000...0001 0000...0001也就是等于1
最后2+1确实等于3!!!
再测几组数:
无符号整型 1 − 2 1-2 1−2 2 : 0000...0010 − 2 : 1111...1110 + 1 = 1111...1111 ( 4294967295 ) 2:0000...0010\ \ \ -2:1111...1110\ +\ 1 \ =\ 1111...1111(4294967295) 2:0000...0010 −2:1111...1110 + 1 = 1111...1111(4294967295)
无符号整型 1 − 3 1-3 1−3 3 : 0000...0011 − 3 : 1111...1101 + 1 = 1111...1110 ( 4294967294 ) 3:0000...0011\ \ \ -3:1111...1101\ +\ 1 \ =\ 1111...1110(4294967294) 3:0000...0011 −3:1111...1101 + 1 = 1111...1110(4294967294)
无符号整型小数减大数根据Mod P的值可逆
- 辅助理解哈希
将字符串“654321”===>十进制
str: | NULL | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|
hash | 0 | ||||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
hash[0] = 0;
hash[1] = hash[0] * 10 + str[1] = 6
hash[2] = hash[1] * 10 + str[2] = 65
hash[3] = hash[2] * 10 + str[3] = 654
…………………………
hash[6] = hash[5] * 10 + str[6] = 654321
最终hash[6]存放的就是整个字符串的哈希值(前缀和方式计算)。
如何取子串的hash值?
子串区间
[
l
,
r
]
子串区间[l, r]
子串区间[l,r]
h
a
s
h
[
r
]
−
h
a
s
h
[
l
−
1
]
∗
p
(
r
−
l
+
1
)
hash[r] - hash[l - 1] * p ^{(r - l + 1)}
hash[r]−hash[l−1]∗p(r−l+1)
aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2
Yes
No
Yes
输入l1 r1 l2 r2,判断[l1, r1]区间的字符串是否等于[l2, r2]
/*************************************************************************
> File Name: h275.cpp
> Author: luzelin
> Mail: luzelin1024@163.com
> Created Time: Sun 30 Oct 2022 01:18:11 PM CST
************************************************************************/
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1e6;
#define int unsigned long long
int BKDR[N + 5], PPow[N + 5], P = 31;
char str[N + 5];
void Init_BKDR() {
PPow[1] = P;
for (int i = 1; str[i]; ++i) {
BKDR[i] = BKDR[i - 1] * P + (int)str[i];
if (i - 1) PPow[i] = PPow[i - 1] * P;
}
return ;
}
#define BKDR(left, right) ({\
BKDR[right] - BKDR[left - 1] * PPow[right - left + 1];\
})
signed main() {
scanf("%s", str + 1);
Init_BKDR();
int m, ll, lr, rl, rr;
scanf("%llu", &m);
while (m--) {
scanf("%llu%llu%llu%llu", &ll, &lr, &rl, &rr);
if (BKDR(ll, lr) == BKDR(rl, rr)) puts("Yes");
else puts("No");
}
return 0;
}
#undef int