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

洛谷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 !
评论
  目录