【BZOJ2565】最长双回文串

Description

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

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

Solution

这题就是求对于每个前缀 S[1..i] ,求它的最长回文后缀,然后正反做一次,枚举分界点对答案取最大值即可。

那么有经典回文串做法manacher(虽说中间插一些美刀不太美观),这里就不多讲了。

还有这题就是回文树模板题,直接查询当前加入的字符所对应的节点(即为当前前缀的最长回文后缀)长度,也是正反做一遍即可。

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
const int N=100010;
char s[N];
int z[N],ln=0;
int next[N][26],fail[N],len[N],last=0,tot;
int suf[N];
int build(int l){
    tot++;
    memset(next[tot],0,sizeof(next[tot]));
    len[tot]=l;
    return tot;
}
void init(){
    tot=-1;
    build(0),build(-1);
    last=ln=0;
    z[ln]=-1,fail[0]=1;
}
int getfail(int x){
    while(z[ln-len[x]-1]!=z[ln]) x=fail[x];
    return x;
}
void add(int c)
{
    c-='a';
    z[++ln]=c;
    int cur=getfail(last);
    if(!next[cur][c])
    {
        int now=build(len[cur]+2);
        fail[now]=next[getfail(fail[cur])][c];
        next[cur][c]=now;
    }
    last=next[cur][c];
}
int main()
{
    freopen("dpal.in","r",stdin);
    freopen("dpal.out","w",stdout);
    scanf("%s",s+1);
    int l=strlen(s+1);
    init();
    fo(i,1,l) add(s[i]),suf[i]=len[last];
    memset(len,0,sizeof(len));
    memset(fail,0,sizeof(fail));
    memset(z,0,sizeof(z));
    init();
    int ans=0;
    fd(i,l,2) add(s[i]),ans=max(ans,len[last]+suf[i-1]);
    printf("%d",ans);
}
posted @ 2017-07-13 21:22  sadstone  阅读(41)  评论(0编辑  收藏  举报