一些小总结:
回文树的每个点表示一个本质不同的字符串。
有两个根,一个表示长度为0的字符串,一个表示长度为-1的字符串。
一条边意味着在两边同时添加一个字符c。
后缀链接指向后缀最长回文串。
在串S的末尾添加一个字符c,最多产生一个本质不同的回文串,只可能是现在的后缀最长回文串。
在回文树建树过程中查找后缀最长回文串以及后缀链接时指针只会向右移(添加字符只会+1),所以复杂度是O(n)的。
模板题:BZOJ3676: [Apio2014]回文串
我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最大出现值。
加入字符时在对应的回文串上cnt+1,最后自底向上传,记录cnt*len的最大值即可。
Code:
#include<bits/stdc++.h>
#define maxn 300005
#define maxc 26
using namespace std;
int n;
char s[maxn];
namespace PAM{
int ch[maxn][maxc],fail[maxn]={1,1},len[maxn]={0,-1},last,tot=1;
int cnt[maxn];
void extend(int x){
int p=last,v=s[x]-'a';
while(s[x-len[p]-1]!=s[x]) p=fail[p];
if(!ch[p][v]){
int q=fail[p];len[++tot]=len[p]+2;
while(s[x-len[q]-1]!=s[x]) q=fail[q];
fail[tot]=ch[q][v];//q may be 1, then ch[q][v]=0, so ch[p][v] later assign.
ch[p][v]=tot;
}
cnt[last=ch[p][v]]++;
}
void solve(){
long long ans=0;
for(int i=tot;i>1;i--)
cnt[fail[i]]+=cnt[i],ans=max(ans,1ll*cnt[i]*len[i]);
printf("%lld\n",ans);
}
}
int main()
{
scanf("%s",s+1),n=strlen(s+1);
for(int i=1;i<=n;i++) PAM::extend(i);
PAM::solve();
}
理性愉悦系列(过于硬核导致直接自闭 ):
国家集训队2017论文 《回文树及其应用》翁文涛
可持久化
硬核题目
最小回文分解