Luogu P4287 [SHOI2011]双倍回文题解
Description
记字符串的倒置为 \(w^R\)。例如 \((abcd)^R = dcba\)。
“双倍回文”为形如 \(ww^Rww^R\) 的字符串。
给定一个字符串 \(s\),求 \(s\) 的最长的双倍回文子串长度。
Solution
观察双倍回文的性质,发现双倍回文整体是一个回文串,它的一半也是一个回文串。考虑找到所有满足条件的双倍回文。
对于每个 \(x\),找到最远的 \(y\),其中 \(x\) 为双倍回文字串的中点,\(y\) 为右端点。
答案即为 \(max\{(y-x)\times 2\}\)
对于每对 \((x,y)\),需要满足
\(f_x \ge 2 \times (y-x)\)
\(f_y \ge y-x\)
其中 \(f_i\) 表示以 \(i\) 为回文中心的最长回文串长度,用 \(manacher\) 预处理即可。
整理上面的式子可得
\(y\le x + \dfrac{f_x}{2}\)
\(f_y-y \ge x\)
所以我们把 \(f_y-y\) 排个序,这样枚举 \(x\) 时,\(y\) 只会变多,不会减少,开个 \(set\) 维护一下,将当前满足条件2的 \(y\) 加入 \(set\),再二分找答案。
注意双倍回文要保证 \(f_x\) 为偶数,并且 \(y-x\) 也为偶数。
Code
#include <iostream>
#include <cstdio>
#include <set>
#include <algorithm>
using namespace std;
const int N = 2e6 + 5;
int n;
char s[N];
int f[N], ans;
struct node
{
int val, id;
friend bool operator < (node x, node y)
{
return x.val < y.val;
}
}a[N];
set <int> S;
void read(char s[])
{
s[0] = '@', s[1] = '#';
n = 1;
char c = getchar();
while(c < 'a' || c > 'z') c = getchar();
while(c >= 'a' && c <= 'z') s[++n] = c, s[++n] = '#', c = getchar();
return;
}
void manacher()
{
for(int i = 1, r = 0, mid = 0; i <= n; i++)
{
if(i <= r) f[i] = min(f[(mid << 1) - i], r - i + 1);
while(s[i + f[i]] == s[i - f[i]]) f[i]++;
if(i + f[i] > r) r = i + f[i] - 1, mid = i;
}
return;
}
void solve()
{
for(int i = 1; i <= n; i++)
f[i]--, a[i].val = i - f[i], a[i].id = i;
sort(a + 1, a + 1 + n);
for(int i = 1, j = 1; i <= n; i++)
{
while(j <= n && a[j].val <= i) S.insert(a[j].id), j++;
if(f[i] & 1) continue;
auto pos = --S.upper_bound(i + f[i] / 2);
if((*pos - i) % 2 == 0) ans = max(ans, (*pos - i) << 1);
}
printf("%d\n", ans);
return;
}
int main()
{
scanf("%d", &n);
read(s);
manacher();
solve();
return 0;
}
$$A\ drop\ of\ tear\ blurs\ memories\ of\ the\ past.$$