洛谷 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
",ans);
    return 0;
}

2018.10.31

原文地址:https://www.cnblogs.com/butterflydew/p/9881263.html