一些小總結:
迴文樹的每個點表示一個本質不同的字符串。
有兩個根,一個表示長度爲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論文 《迴文樹及其應用》翁文濤
可持久化
硬核題目
最小回文分解