P4555

· · 个人记录

P4555

题意

给你一个字符串 a,求出 a 的最长 双回文子串

若一个串是 双回文子串 ,那么它可以从某个地方断开变成两个回文串

思路

定义 p[i]:以 i 为回文中心的最长回文半径 ,l[i]i 左侧以 i 结尾的最长回文串,r[i] 同理

先跑一遍 manacher

易得一个最长 双回文子串 必须是由两个回文串拼起来的

所以在 manacher 的时候可以通过求得的 p[i] 更新 l[i]r[i]

最后在枚举一遍用 l[i]+r[i] 更新答案

注意:最后枚举更新答案时枚举的点是插进字符串的符号的位置,这样才能把左右两侧回文串拼起来

code

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;

#define int long long

int n,m=2,ans;
char a[N],s[N*2];
int p[N*2],cnt[N];
int l[N*2],r[N*2];

inline void change(){
    s[0]='$',s[1]='|';
    for(int i=0;i<n;++i){
        s[m++]=a[i];
        s[m++]='|';
    }
    s[m]='%';
    n=m;
}

inline void manacher(){
    int mx=1,mid=1;
    for(int i=1;i<=n;++i){
        if(i<mx)
            p[i]=min(p[mid*2-i],mx-i);
        else
            p[i]=1;
        for(;s[i-p[i]]==s[i+p[i]];++p[i]);
        if(i+p[i]>mx){
            mx=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);
    }
}

signed main(){
    scanf("%s",a);
    n=strlen(a);
    m=2;
    change();
    manacher();
    for(int i=1;i<=n;i+=2)
        r[i]=max(r[i],r[i-2]-2);
    for(int i=n;i>=1;i-=2)
        l[i]=max(l[i],l[i+2]-2);
    for(int i=1;i<=n;i+=2)
        if(l[i]&&r[i])
            ans=max(ans,l[i]+r[i]);
    cout<<ans;
}