洛谷 P4555 [国家集训队]最长双回文串 解题报告

P4555 [国家集训队]最长双回文串

题目描述

顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为abc,逆序为cba,不相同)。

输入长度为\(n\)的串\(S\),求\(S\)的最长双回文子串\(T\),即可将\(T\)分为两部分\(X\)\(Y\),(\(|X|,|Y|≥1\))且\(X\)\(Y\)都是回文串。

输入输出格式

输入格式:

一行由小写英文字母组成的字符串\(S\)

输出格式:

一行一个整数,表示最长双回文子串的长度。

说明

对于\(100\%\)的数据,\(2≤|S|≤10^5\)


按套路先把每个位置为中心的回文串长度用\(manacher\)求出来。

然后比较暴力的想法是直接线段树做一下,虽然也可以通过不过不优雅。

预处理出每个点为开头或结尾的最长回文串长度,然后枚举断点就可以了。

如何预处理?发现可以先通过中心的最长回文串预处理一部分,然后扫描一遍,更新非最长的长度。

具体的说,比如\(pre_i\)代表\(i\)开头的最长回文串,那么显然是有\(pre_i=max(pre_{i-2}-2,pre_i)\)的(这里已经插入了'#')

这个技巧用的比较多了,要注意使用。


Code:

#include <cstdio>
#include <cstring>
const int N=2e5+10;
int a[N],t[N],L[N],R[N],n,ans,r;
char s[N],c[N];
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
int main()
{
    memset(t,0x3f,sizeof(t));
    scanf("%s",c+1);
    n=strlen(c+1);
    s[0]=s[1]='$';
    for(int i=1;i<=n;i++) s[i*2]=c[i],s[i*2+1]='$';
    n=(n<<1)+1;
    for(int rig=0,mid,i=1;i<=n;i++)
    {
        a[i]=i>rig?1:min(rig-i,a[(mid<<1)-i]);
        while(s[i-a[i]]==s[i+a[i]]) ++a[i];
        --a[i];
        if(a[i]+i>rig) rig=a[i]+i,mid=i;
        L[i-a[i]]=max(L[i-a[i]],a[i]);
        R[i+a[i]]=max(R[i+a[i]],a[i]);
    }
    for(int i=3;i<=n;i+=2) L[i]=max(L[i],L[i-2]-2);
    for(int i=n;i>0;i-=2) R[i]=max(R[i],R[i+2]-2);
    for(int i=1;i<=n;i+=2) ans=max(ans,L[i]+R[i]);
    printf("%d\n",ans);
    return 0;
}

2018.10.31

posted @ 2018-10-31 09:52  露迭月  阅读(217)  评论(0编辑  收藏  举报