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);
}