字符串哈希

字符串哈希

将字符串转换为一个哈希值,从而判断两个字符串是否相同。

模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int P = 131;
const int _ = 1510;
ull p[_], h[_];
int n;
char s[_];
void init(){
p[0] = 1, h[0] = 0;
for(int i = 1; i <= n; i++){
p[i] = p[i-1] * P;
h[i] = h[i-1] * P + s[i];
}
}

ull get(int l, int r){
return h[r] - h[l-1] * p[r-l+1];
}

bool substr(int l1, int r1, int l2, int r2){
return get(l1, r1) == get(l2, r2);
}

上面是P取131或13331,然后模数采用 unsigned long long 的自动溢出机制。

练习题:https://codeforces.com/contest/1943/problem/B。div1的B,难度为2000,中等难度。

这题的重点实际上并不在于字符串哈希,而是在于判 aaa 和 ababa 这种情况。

但是对于整个子串的情况,需要特别判断是否为回文串。

如果你采用上述的哈希方式,你会发现你WA了。原因自然是哈希冲突。

然后我试了很多组 P 和 MOD,最后终于得到了一个不冲突的方案(2333和998244853):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int P = 2333;
const int _ = 200010;
const ull mod = 998244853;
ull p[_], h[_], hh[_];
int n, q;
string s;
set<int> even, odd;
void init(){
p[0] = 1, h[0] = hh[0] = 0; even.clear(); odd.clear();
for(int i = 1; i <= n; i++){
p[i] = p[i-1] * P % mod;
h[i] = h[i-1] * P + s[i-1]; h[i] %= mod;
hh[i] = hh[i-1] * P + s[n-i]; hh[i] %= mod;
if(i < n && s[i-1] != s[i]) odd.insert(i);
if(i < n - 1 && s[i-1] != s[i+1]) even.insert(i);
}
}

ull geth(int l, int r){
return (h[r] - h[l-1] * p[r-l+1] % mod + mod) % mod;
}
ull gethh(int l, int r){
int rr = n - l + 1, ll = n - r + 1;
return (hh[rr] - hh[ll-1] * p[rr-ll+1] % mod + mod) % mod;
}

int main(){
ios::sync_with_stdio(0);
cin.tie();
int T; cin >> T;
while(T--){
cin >> n >> q;
cin >> s;
init();
while(q--){
int l, r;
cin >> l >> r;
int len = r - l + 1;
ll ans = 0;
if(geth(l, r) != gethh(l, r)) ans = len;
auto it = odd.lower_bound(l);
auto it2 = even.lower_bound(l);
if(it == odd.end() || (*it) >= r){
;
}else{
if(it2 == even.end() || (*it2) >= r - 1)
ans += 1ll * (len - 1) / 2 * ((len - 1) / 2 + 1);
else
ans += 1ll * (len - 1) * len / 2 - 1;
}
cout << ans << '\n';
}
}
return 0;
}

事实说明哈希冲突还是很玄学的。因此需要考虑更好的判回文串的方式:Manacher。

有空再写!