【6】Hash 学习笔记
简单来说,Hash 就是通过一个函数,将每一个字符串一一对应到一个整数上来加快比较速度的算法。
Hash 算法的优点是可以快速解决正解是其他字符串算法复杂问题,缺点是可能会被卡。因此,人们创造了多种 Hash 方法。
这里介绍较优的一种:BKDRHash。
现在给定一个字符串
此时,我们可以寻找一个底数
具体地,假设
知道了该字符串怎么求 Hash 之后,我们可以求出其任意一个子串的 Hash 值。
我们定义
于是,我们可以在
那么我们如何找到
回想一下我们是怎么在一个十进制数中找到一段的:例如,我们要在数
(
上面竖式可以写成:
又:在应用中一般使用
P3370 【模板】字符串哈希
给定
直接将每个字符串的 Hash 值求出来,然后丢进一个数组。于是问题就变成了有多少个不同的整数。排序即可。
时间复杂度
#include <bits/stdc++.h>
using namespace std;
const int base = 919,mod = 998244353;
int hhash(string s) {
int x = 0;
for(int i = 0;i < s.size();i++)
x = (base * x + s[i] - '0') % mod;
return x;
}
int a[200005];
int main() {
int n; cin >> n;
for(int i = 1;i <= n;i++) {
string x; cin >> x;
a[i] = hhash(x);
}
sort(a+1,a+n+1);
int cnt = 0;
for(int i = 1;i <= n;i++)
if(a[i] != a[i-1]) cnt++;
cout << cnt;
return 0;
}
P10468 兔子与兔子
给定字符串
可以直接用刚才的求子串哈希值的方法,只需预处理出
#include <bits/stdc++.h>
using namespace std;
using ull = unsigned long long; const ull base = 131;
ull h[1000005], y[1000005]; ull Hash(int l, int r) {
return h[r] - h[l-1] * y[r-l+1];
} int main() {
string s; cin >> s; s = ' ' + s;
for(int i = 1;i < s.size();i++) h[i] = h[i-1] * base + (s[i] - 'a') + 1;
y[0] = 1; for(int i = 1;i < s.size();i++) y[i] = y[i-1] * base;
int q; cin >> q; while(q--) { int l1, r1, l2, r2; cin >> l1 >> r1 >> l2 >> r2;
if(Hash(l1, r1) == Hash(l2, r2)) cout << "Yes\n"; else cout << "No\n";
} return 0;
}
P10469 后缀数组
求所有的
可以考虑对于每一个位置二分答案,二分长度之后拿哈希判断是否相等。时间复杂度
(P.S. 将 sort 改成 stable_sort 就可以过原板子 qwq)
#include <bits/stdc++.h>
using namespace std;
string s; int n;
using ull = unsigned long long;
ull hs[300005], p[300005];
const ull base = 131;
ull qry(int l, int r) { return hs[r] - hs[l-1] * p[r-l+1]; }
struct SA { int x; } a[300005];
bool cmp(SA x, SA y) {
int xx = x.x, yy = y.x;
//看[xx, n] 和 [yy, n] 的字典序大小
int l = 0, r = n-max(xx, yy);
if(r == 1) return (s[xx] != s[yy] ? s[xx] < s[yy] : s[xx+1] < s[yy+1]);
while(l < (r-1)) {
int mid = (l + r) >> 1;
if(qry(xx, xx+mid-1) == qry(yy, yy+mid-1)) l = mid;
else r = mid - 1;
} if(qry(xx, xx+r-1) == qry(yy, yy+r-1)) return s[xx+r] < s[yy+r];
else return s[xx+l] < s[yy+l];
} int main() { p[0] = 1;
cin >> s; n = s.size(); s = s + ' '; hs[0] = s[0];
for(int i = 1;i < n;i++)
hs[i] = hs[i-1] * base + s[i], p[i] = p[i-1] * base;
for(int i = 0;i < n;i++) a[i].x = i;
sort(a, a+n, cmp);
for(int i = 0;i < n;i++) cout << a[i].x << " \n"[i == n-1];
cout << 0 << " "; for(int i = 1;i < n;i++) {
int xx = a[i].x, yy = a[i-1].x;
int l = 0, r = n-max(xx, yy);
if(r == 1) cout << (s[xx] == s[yy] ? 1 : 0) << " ";
else {
while(l < (r-1)) {
int mid = (l + r) >> 1;
if(qry(xx, xx+mid-1) == qry(yy, yy+mid-1)) l = mid;
else r = mid - 1;
} if(qry(xx, xx+r-1) == qry(yy, yy+r-1)) cout << r << " ";
else cout << l << " ";
}
}
return 0;
}