字符串哈希

方法

通常采用多项式 Hash 的方法,也就是说将字符串看做一个 b 进制的数。

进制数选择(大于所有字符对应的数字的最大值,且为质数),如:131 233 13331 19260817 等。

模数选择(双\(10^9\)的模数或者直接自然溢出):19260817 19660813 等。

然后就可以愉快的哈希了!

code(自然溢出)
ull hashh (string s) {
	ull res = 0;
	for (int i = s.size () - 1; i >= 0; i --) {
		res = (base * res + (s[i] - 'a'));
	}
	return res;
}

子串哈希

如何得到子串的哈希值呢?

可以先对字符串的每个前缀进行预处理,根据哈希方法:

定义字符串前缀的哈希值为 \(p_i=s_1×b^{i-1}+s_2×b^{i-2}+…+s_i\)

那么子串 \(s_{l-r}\) 的哈希值就为 \(p_{l-r}=p_{r}-p_{l-1}×b^{r-l+1}\)

这里的 \(b^{r-l+1}\) 可以通过预处理然后 \(O(1)\) 查询,也可以直接快速幂
\(O(\log n)\) 搞定。

code
void hashh (string s) {
	h[0] = s[0] - 'a' + 1;
	for (int i = 1; i < s.size(); i ++) {
		h[i] = h[i - 1] * base + s[i] - 'a' + 1;
	}
}

cin >> l >> r;
cout << h[r] - h[l - 1] * qpow (base, r - l + 1);

可以应用到字符串匹配。


最长回文子串

采用二分答案,预处理正串的哈希值和反串的哈希值,枚举对称轴即可。

P3805模板
#include <bits/stdc++.h>

using namespace std;
typedef unsigned long long ull;
const int N = 11000007;
const int base = 233;
ull h1[N], h2[N], pw[N];
char s[N];
ull g1 (int l, int r) {
	return h1[r] - h1[l - 1] * pw[r - l + 1];
}
ull g2 (int l, int r) {
	return h2[r] - h2[l - 1] * pw[r - l + 1];
}

int main () {
	scanf ("%s", s + 1);
	int len = strlen (s + 1);
	h1[0] = h2[len + 1] = 0;
	pw[0] = 1;
	for (int i = 1; i <= len; i ++) {
		h1[i] = h1[i - 1] * base + s[i] - 'a' + 1;
	}
	int l = 1, r = len;
	while (l <= r) {
		int c1 = s[l], d1 = s[r];
		s[l] = d1, s[r] = c1;
		l ++, r --;
	}
	for (int i = 1; i <= len; i ++) {
		h2[i] = h2[i - 1] * base + s[i] - 'a' + 1;
	}
	l = 1, r = len;
	while (l <= r) {
		int c1 = s[l], d1 = s[r];
		s[l] = d1, s[r] = c1;
		l ++, r --;
	}
	for (int i = 1; i <= len; i ++) {
		pw[i] = pw[i - 1] * base;
	}
	int ans = 1;
	for (int i = 1; i <= len; i ++) {
		int la = 1, ra = min (i - 1, len - i), t = 0;
		while (la <= ra) {
			int mid = (la + ra) >> 1, ll = i - mid, rr = i + mid;
			if (g1 (ll, rr) == g2 (len - rr + 1, len - ll + 1)) {
				la = mid + 1;
				ans = max (ans, mid * 2 + 1);
			}
			else {
				ra = mid - 1;
			}
		}
		t = 0;
		int lb = 0, rb = min (i - 1, len - i - 1);
		while (lb <= rb) {
			int mid = (lb + rb) >> 1, ll = i - mid, rr = i + mid + 1;
			if (g1 (ll, rr) == g2 (len - rr + 1, len - ll + 1)) {
				lb = mid + 1;
				ans = max (ans, mid * 2 + 2);
			}
			else {
				rb = mid - 1;
			}
		}
	}
	cout << ans << endl;
	return 0;
}

最长公共子串

预处理每个字符串长度为 \(x\) 的哈希值放入哈希表。

易得长度 \(k\) 可以二分答案,所以可以解决。

posted @ 2023-11-12 17:12  21_devoted  阅读(34)  评论(0)    收藏  举报