P4287 [SHOI2011]双倍回文

· · 题解

关于双倍回文这道题,可以算是一道回文自动机的板子题

但是需要稍微魔改一下,在构造回文自动机的过程中顺带求出一个 trans 指针

当我们新建一个节点后,如果它的长度小于等于 2,那么这个节点的 $trans$ 指针指向它的 $fail$ 节点 否则的话,我们同理从它父亲的 $trans$ 指针指向的节点开始跳 $fail$ 指针 直到跳到某一个节点所表示的回文串的两侧都能扩展这个字符 并且拓展后的长度小于等于当前节点长度的一半 那么新建节点的 $trans$ 的指针就指向该节点的儿子 有关回文自动机的介绍及 $fail$ 指针的求法,详见[我的博客](https://www.cnblogs.com/Polaris5452830/p/10498625.html) 求完这个 $trans$ 指针之后,这个题就变得非常容易了 我们考虑一个字符串满足双倍回文 当且仅当它的 $trans$ 指针指向的节点所表示的回文串长度刚好是这个字符串长度的一半,并且这个 $trans$ 指针指向的节点所表示的回文串长度为偶数 这样我们就可以枚举每个节点,不停的更新答案 代码如下 ```cpp #include<cstdio> #include<algorithm> const int maxn=555555; char s[maxn]; int cnt,last; int trans[maxn]; int son[maxn][26]; int len[maxn],fail[maxn]; int new_node(int length) { len[++cnt]=length; return cnt; } int get_fail(int pre,int now) { while(s[now-len[pre]-1]!=s[now]) pre=fail[pre]; return pre; } void build_PAM() { cnt=1,last=0; len[0]=0,len[1]=-1; fail[0]=1,fail[1]=1; for(int i=1;s[i];i++) { int cur=get_fail(last,i); if(!son[cur][s[i]-'a']) { int now=new_node(len[cur]+2); fail[now]=son[get_fail(fail[cur],i)][s[i]-'a']; son[cur][s[i]-'a']=now; //顺带求出trans指针 if(len[now]<=2) trans[now]=fail[now]; else { int tmp=trans[cur]; while(s[i-len[tmp]-1]!=s[i]||((len[tmp]+2)<<1)>len[now]) tmp=fail[tmp]; //注意拓展后的长度为len[tmp]+2 trans[now]=son[tmp][s[i]-'a']; } } last=son[cur][s[i]-'a']; } } int n,ans; int main() { scanf("%d",&n); scanf("%s",s+1); build_PAM(); for(int i=2;i<=cnt;i++)//枚举所有节点 if(((len[trans[i]]<<1)==len[i]&&len[trans[i]]%2==0)) ans=std::max(ans,len[i]);//更新答案 printf("%d\n",ans); return 0; } ```