Luogu P4287 [SHOI2011]双倍回文题解

Link

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;
}
posted @ 2021-10-04 09:29  Acestar  阅读(46)  评论(0编辑  收藏  举报