回文自动机
构造
和AC自动机及其类似
相比Manacher,它还可以统计某个回文子串的数量,结尾为\(i\)的回文串数量,大小(更方便)
节点
一个字符串总共有有\(n\)个本质不同的回文串
证明:对于每个右端点\(i\),每次只有多出至多\(1\)个回文串,多出的是最长的回文串
因为其他的均能通过在最长串中点对称,找到相应的右端点在之前的回文串,所以至多只有\(1\)个
根据上述结论,只要找出所有右端点对应的最长回文串,即可找到所有本质不同的回文串(Manacher扩展r时)
这样就可以以本质不同的回文串作为节点,正好完全包括了全部的字符串且不重不漏
fail指针——回文树
显然fail指针指向上一个回文串后缀
构造(略)
模板
Luogu P3649 [APIO2014]回文串
注意nxt一定最后赋值
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+5;
int n,lst,tot,len[N],pa[N],nxt[N][26],c[N],b[N];
ll a[N];
char s[N];
inline void PAM(int ch,int n) {
int u=lst;
while(s[n-len[u]-1]!=s[n]) u=pa[u];
if(!nxt[u][ch]) {
int now=++tot; len[now]=len[u]+2;
int v=pa[u];
while(s[n-len[v]-1]!=s[n]) v=pa[v];
pa[now]=nxt[v][ch];
nxt[u][ch]=now;
}
lst=nxt[u][ch];
a[lst]++;
}
int main(){
scanf("%s",s+1); n=strlen(s+1);
pa[1]=0,pa[0]=1,len[1]=-1; lst=0; tot=1;
for(int i=1;i<=n;i++) {
PAM(s[i]-='a',i);
}
for(int i=2;i<=tot;i++) c[len[i]]++;
for(int i=2;i<=n;i++) c[i]+=c[i-1];
for(int i=tot;i>1;i--) {
b[c[len[i]]--]=i;
}
for(int i=tot-1;i>=1;i--) {
int u=b[i]; a[pa[u]]+=a[u];
}
ll ans=1;
for(int i=2;i<=tot;i++) {
ans=max(ans,a[i]*len[i]);
}
printf("%lld\n",ans);
return 0;
}

回文自动机
浙公网安备 33010602011771号