嘘~ 正在从服务器偷取页面 . . .

洛谷P4287 [SHOI2011]双倍回文 题解


洛谷P4287 [SHOI2011]双倍回文 题解

题目链接:P4287 [SHOI2011]双倍回文

题意

记字符串 $w$ 的倒置为 $w^R$ 。例如 $(\tt{abcd})^R=\tt{dcba}$ , $(\tt{abba})^R=\tt{abba}$ 。

对字符串 $x$ ,如果 $x$ 满足 $x^R=x$ ,则称之为回文;例如 $\tt{abba}$ 是一个回文,而 $\tt{abed}$ 不是。

如果 $x$ 能够写成的 $ww^Rww^R$ 形式,则称它是一个“双倍回文”。换句话说,若要 $x$ 是双倍回文,它的长度必须是 $4$ 的倍数,而且 $x$ , $x$ 的前半部分, $x$ 的后半部分都要是回文。例如 $\tt{abbaabba}$ 是一个双倍回文,而 $\tt{abaaba}$ 不是,因为它的长度不是 $4$ 的倍数。

$x$ 的子串是指在$x$中连续的一段字符所组成的字符串。例如 $\tt{be}$ 是 $\tt{abed}$ 的子串,而 $\tt{ac}$ 不是。

$x$ 的回文子串,就是指满足回文性质的 $x$ 的子串。

$x$ 的双倍回文子串,就是指满足双倍回文性质的 $x$ 的子串。

你的任务是,对于给定的字符串,计算它的最长双倍回文子串的长度。

$n \le 500000$

考虑Manacher求解

对于修改后字符串的 $i$ 位置的初始回文半径 $p_i$ (暴力拓展前的半径)

如果 $i-p_i+1 \le \text{mid}$ ,则存在一个极大双倍回文子串

这个极大双倍回文子串在修改后的串中,

以「以 $\text{mid}$ 为中心的回文串」的形式存在

注意题目要求的是 $4$ 倍数的串,则对于刚才的求解方法,

只要限制 $i$ 为奇数时更新答案、$\text{mid}$ 和 $r$

此时 $i$ 的回文半径 $p_i^{\prime}-1$ 一定是偶串的长度

注意 $r$ 相同时,不要优先更新 $\text{mid}$

否则根据贪心,不难发现会失去某些极大值,导致答案出错

这里给个hack(想了我几个小时才想出来

input:
12
abcbbbbbbbba

output:
8

时间复杂度 $O(n)$

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(5e5+15)

char s[N<<1];
int n,p[N<<1];
void Manacher(int l)
{
    int r=1,mid=1,ans=0;
    // 这里的i+=2可以写i++
    // 但是要把下面两个if都加上 (i&1) && ...
    // 没必要。还慢。
    for(int i=1; i<=l; i+=2)
    {
        p[i]=(i<=r)?min(p[(mid<<1)-i],r-i+1):1;
        if(i<=r&&i-p[i]+1<=mid) ans=max(ans,2*(i-mid));
        // cout << i << " " << mid << " " << r << '\n';
        while(s[i-p[i]]==s[i+p[i]]) ++p[i];
        if(i+p[i]-1>r)r=i+p[i]-1,mid=i;
        // cout << i << " " << mid << " " << r << '\n';
    }
    cout << ans << '\n';
    // for(int i=1; i<=l; i++)
    //     cout << p[i] << " \n"[i==l];
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("P4287.out","w",stdout);
    cin >> n >> (s+1);
    s[0]='$';
    for(int i=n; i; i--)
        s[i*2+1]='#',s[i*2]=s[i];
    s[1]='#';
    Manacher(n*2+1);
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录