P4324 JSOI2016 扭动的回文串 - 洛谷 (luogu.com.cn)
想了很久 Manacher 解法没成功,一看题解可以用纯哈希过。
前两种情况无论你使用哈希还是马拉车,都可以很快解决,所以着重看第三种情况。
回文串相关的题目一定和枚举中心点相关,往这方面想。
如何在确定中心点 的情况下,让两个串的匹配最大化?
假设当前中心点在 A,如果和中心点连着的一段下标对应的字符 A 串和 B 串的一样,
我们优先选 A 的,反之优先选 B 的。
如何让这种"连着"最大化?那其实就是在找在 A 中以 i 为中心的最长回文半径。
以这个半径作为固定要选在 A 的字符,往左边向 A 拓展,往右边向 B 拓展,看是否能匹配。
这个匹配的过程可以现用现算,二分就行。
可以证明这样一定是最优解之一,中心点在 B 同理,偶数情况也不难。
由于这个做法一定带一个 log,所以求回文半径也不用马拉车了,一样用二分哈希。
怎么说呢?是一个比较巧妙的解法吧,第一次看有点想不到,但其实思路挺自然。
最主要是想到回文半径的基础上配对一定是最优解之一。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL; // 自然溢出
const int N = 1e5 + 10;
const ULL P = 13331; // 哈希基数
int n;
char sa[N], sb[N];
ULL ha[N], hb[N], rha[N], rhb[N];
ULL facp[N];
void init() {
ha[0] = 0; hb[0] = 0;
for (int i = 1; i <= n; i ++) {
ha[i] = ha[i - 1] * P + (sa[i] - 'A' + 1);
hb[i] = hb[i - 1] * P + (sb[i] - 'A' + 1);
}
rha[n + 1] = 0; rhb[n + 1] = 0;
for (int i = n; i >= 1; i --) {
rha[i] = rha[i + 1] * P + (sa[i] - 'A' + 1);
rhb[i] = rhb[i + 1] * P + (sb[i] - 'A' + 1);
}
facp[0] = 1;
for (int i = 1; i <= n + 1; i ++) {
facp[i] = facp[i - 1] * P;
}
}
ULL get_h(ULL h[], int l, int r) {
return h[r] - h[l - 1] * facp[r - l + 1];
}
ULL get_rh(ULL rh[], int l, int r) {
return rh[l] - rh[r + 1] * facp[r - l + 1];
}
int get_expand(ULL h[], ULL rh[], int ql, int qr) {
int l = 0, r = min(ql, n - qr + 1), res = l;
while (l <= r) {
int mid = (l + r) >> 1;
ULL ta = get_h(h, ql - mid + 1, ql);
ULL tb = get_rh(rh, qr, qr + mid - 1);
if (ta == tb) {
l = mid + 1;
res = mid;
}
else {
r = mid - 1;
}
}
return res;
}
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
cin >> (sa + 1) >> (sb + 1);
init();
int ans = 1;
for (int i = 2; i <= n; i ++) {
int la = get_expand(ha, rha, i, i);
int lb = get_expand(hb, rhb, i, i);
ans = max(ans, la * 2 - 1 + get_expand(ha, rhb, i - la, i + la - 1) * 2);
// 在 A 里 [i - la + 1, i + la - 1] 都是回文半径,B 的第一个和 A 的最后一个共用一个下标
ans = max(ans, lb * 2 - 1 + get_expand(ha, rhb, i - lb + 1, i + lb) * 2);
// 在 B 里 [i - lb + 1, i + lb - 1] 都是回文半径,A 的最后一个和 B 的第一个共用一个下标
}
for (int i = 1; i < n; i ++) {
int la = get_expand(ha, rha, i, i + 1);
int lb = get_expand(hb, rhb, i, i + 1);
ans = max(ans, la * 2 + get_expand(ha, rhb, i - la, i + la) * 2);
// 在 A 里 [i - la + 1, i + la] 都是回文半径,B 的第一个和 A 的最后一个共用一个下标
ans = max(ans, lb * 2 + get_expand(ha, rhb, i - lb + 1, i + lb + 1) * 2);
// 在 B 里 [i - lb + 1, i + lb] 都是回文半径,A 的最后一个和 B 的第一个共用一个下标
}
cout << ans << "\n";
return 0;
}