回文自动机学习笔记
回文自动机学习笔记
和AC自动机差不多,回文自动机每个节点的含义表示在它的父节点两侧各加上一个儿子字符。并且我们也要维护一个fail指针表示这个节点的最长回文后缀
根
PAM有两个根,奇根和偶根,
偶根的节点编号为 \(0\),所代表的回文串的长度为 \(0\),fail指针指向奇根
奇根的节点编号为 \(1\),所代表的回文串的长度为 \(-1\),fail指针指向自身
fail指针
fail指针表示这个节点的最长回文后缀,构建方法是往上跳fail直到两端能+1。
关于新建节点,也是往上跳fail,没有出边就新建点,并且新节点长度等于这个节点长度+2。
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(int i=(a);i>=(b);--i)
const int inf = 1e9;
using namespace std;
int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
const int N = 1e6+200;
char s[N];
int tot,las;
int fail[N],len[N],pos[N];
int ch[N][26],num[N];
void init(){
las=0,tot=1;
len[0]=0,fail[0]=1;
len[1]=-1,fail[1]=0;
}
void add(int c,int id){
int p=las;
for(;s[id]!=s[id-len[p]-1];p=fail[p]);
if(!ch[p][c]){
int np=++tot,q=fail[p];len[np]=len[p]+2;
for(;s[id]!=s[id-len[q]-1];q=fail[q]);
fail[np]=ch[q][c]; ch[p][c]=np;
num[np]=num[fail[np]]+1;
}
las=ch[p][c];
}
int main(){
scanf("%s",s+1);
init();
int pre=0,l=strlen(s+1);
FOR(i,1,l){
s[i] = (s[i] - 97 + pre) % 26 + 97;
add(s[i]-'a',i);
pos[i]=las;
printf("%d ",num[las]);
pre=num[las];
}
return 0;
}