DestinHistoire

 

BZOJ-2565 最长双回文串(Manacher算法)

题目描述

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

  输入长度为 \(n(1\leq n\leq 10^5)\) 的串 \(s\),求 \(s\) 的最长双回文子串 \(t\),即可将 \(t\) 分为两部分 \(x,y(1\leq |x|,|y|)\),且 \(x\)\(y\) 都是回文串。

分析

  在 \(\text{Manacher}\) 算法中,以位置 \(i\) 为回文中心的最长回文半径为 \(p[i]-1\),在处理 \(p[i]\) 时,同时记录两个信息:\(l[i]\) 表示以位置 \(i\)结尾 的最长回文子串的长度,则 \(l[i+p[i]-1]=\max(l[i+p[i]-1],p[i]-1)\)\(r[i]\) 表示以位置 \(i\)开头 的最长回文子串的长度,则 \(r[i-p[i]+1]=\max(r[i-p[i]+1],p[i]-1)\)

  预处理之后,递推以每个 # 为断点的 \(l[i]\)\(r[i]\)\(l[i]\) 是以位置 \(i\) 为结尾的最长回文子串长度,逆推,\(l[i]=\max(l[i],l[i+2]-2)\)\(r[i]\) 是以位置 \(i\) 为开头的最长回文子串长度,顺推,\(r[i]=\max(r[i],r[i-2]-2)\)

  最后枚举每个 # 为断点,更新最大值即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char a[N],s[N];
int len,p[N],l[N],r[N];
void Manacher()
{
    len=strlen(a);
    s[0]='$';s[1]='#';
    for(int i=0;i<len;i++)
    {
        s[2*i+2]=a[i];
        s[2*i+3]='#';
    }
    int maxright=0,mid=0;
    for(int i=0;i<=2*len+1;i++)
    {
        if(i<maxright)
            p[i]=min(p[mid*2-i],maxright-i);
        else
            p[i]=1;
        while(s[i-p[i]]==s[i+p[i]])
            p[i]++;
        if(i+p[i]>maxright)
        {
            maxright=i+p[i];
            mid=i;
        }
        l[i+p[i]-1]=max(l[i+p[i]-1],p[i]-1);
        r[i-p[i]+1]=max(r[i-p[i]+1],p[i]-1);
    }
}
int main()
{
    scanf("%s",a);
    Manacher();
    for(int i=1;i<=2*len+1;i=i+2)
        r[i]=max(r[i],r[i-2]-2);
    for(int i=2*len+1;i>=1;i=i-2)
        l[i]=max(l[i],l[i+2]-2);
    int ans=-1;
    for(int i=1;i<=2*len+1;i=i+2)
        if(l[i]!=0&&r[i]!=0)
            ans=max(ans,l[i]+r[i]);
    cout<<ans<<endl;
    return 0;
}

posted on 2020-11-20 21:14  DestinHistoire  阅读(95)  评论(0)    收藏  举报

导航