SHOI2011 双倍回文

双倍回文

如果 s 能够写成 wwRwwR 的形式,则称 s 是双倍回文。

s 的长度是 4 的倍数,前后两半都是相同的回文。

对于给定的字符串,计算它的最长双倍回文串的长度。

N<=500000

yyb的题解

发现这个长度为自身一半的的回文串是可以方便地转移的。

对于每个节点,我们维护一个half来表示长度最长的、不超过它长度一半的那个祖先节点

这样子只需要判断一下当前点的half长度是否是一半,并且当前串的长度是四的倍数就好了

找 half 就用 p 的 half 来跳就行了。

#include
using namespace std;
template T read(){
    T x=0,w=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*w;
}
template T read(T&x){
    return x=read();
}
#define co const
#define il inline
typedef long long LL;

co int N=500000+10;
char s[N];
int last=1,tot=1;
int ch[N][26],fa[N]={1,1},len[N]={0,-1},half[N];

int get_fa(int x,int i){
    while(s[i-len[x]-1]!=s[i]) x=fa[x];
    return x;
}
void extend(int i){
    int p=get_fa(last,i);
    int x=ch[p][s[i]-'a'];
    if(!x){
        x=++tot;
        fa[x]=ch[get_fa(fa[p],i)][s[i]-'a'];
        len[x]=len[p]+2;
        ch[p][s[i]-'a']=x;
        if(len[x]==1) half[x]=0;
        else{
            int q=half[p];
            while(s[i-len[q]-1]!=s[i]||(len[q]+2)<<1>len[x]) q=fa[q];
            half[x]=ch[q][s[i]-'a'];
        }
    }
    last=x;
}
int main(){
    int n=read();
    scanf("%s",s+1);
    for(int i=1;i<=n;++i) extend(i);
    int ans=0;
    for(int i=1;i<=tot;++i)
        if(len[half[i]]<<1==len[i]&&len[i]%4==0)
            ans=max(ans,len[i]);
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(SHOI2011 双倍回文)