省选算法学习-回文自动机 && 回文树
前置知识
首先你得会manacher,并理解manacher为什么是对的(不用理解为什么它是$O(n)$,这个大概记住就好了,不过理解了更方便做$PAM$的题)
什么是回文自动机?
回文自动机(Palindrome Automaton),是一类有限状态自动机,能识别一个字符串的所有回文子串
它可简化构建出回文树
回文自动机的构造
网上资料很多,不拿出来一步步说了,说一下数组意义、放个板子
$len[u]$表示节点$u$代表的回文串的长度
$ch[u][c]$代表在$u$的回文串两端添加字符$c$得到的新回文串节点
$fail[u]$表示节点$u$的回文串的最长的回文后缀所在的节点
char s[300010];
int n;
//这个板子里面的num表示当前节点的回文串出现了几遍
namespace pam{
int fail[300010],num[300010],len[300010],ch[300010][26],last,cnt;
inline int newnode(int w){len[++cnt]=w;return cnt;}
void init(){
s[0]=-1;cnt=-1;fail[0]=1;last=0;
newnode(0);newnode(-1);//插入两个根节点,设置fail
}
inline int getfail(int cur,int pos){
while(s[pos-len[cur]-1]!=s[pos]) cur=fail[cur];//跳fail
return cur;
}
void insert(int x){
int c=(s[x]-'a'),cur=getfail(last,x);
if(!ch[cur][c]){//新建节点
int now=newnode(len[cur]+2);
fail[now]=ch[getfail(fail[cur],x)][c];
ch[cur][c]=now;
}
num[last=ch[cur][c]]++;
}
}
几个容易写错的点:
fail初始化的时候,如果多组数据并且在newnode里面初始化信息,那么在init的时候记得把fail放到newnode后面
插入的时候函数传进去的是位置
newnode是len[cur]+2不是+1
一些拓展
节点上
首先显然可以统计这个节点的回文串出现次数
统计次数的时候还要加上$fail$树上子树内的所有节点的出现次数
对于一类回文串拥有某个和其$fail$树有关的性质的题目,可以记录一个$trans$,和跳$fail$一样跳,最后用$bfs$来做$dp$或者递推
关于求最小回文串分解
这个问题是问可以把一个字符串分解成最少多少个回文串
解决的方法:
考虑一个显然的$dp$:$dp[i]=dp[j-1]+1 (s[i...j]=palindrome)$
记录一个$anc[u]$
如果$len[u]-len[fail[u]]==len[fail[u]]-len[fail[fail[u]]]$,那么$anc[u]=anc[fail[u]]$
否则$anc[u]=u$
对于$u$的所有跳上去的$anc$集合$S$,我们发现,这个集合中的元素构成一个等差数列,相邻的两项差代表一种从当前点前面递推到当前点的回文串长度
对于每个回文树节点记录$tmp[u]=min(tmp[i-len[anc[u]],tmp[fail[u]])$,然后用这个$tmp[u]+1$来更新当前节点的$dp$,然后$u=fail[anc[u]]$往上跳,直到到达根
证明网上有论文,这里放个代码
inline void insert(int x){
int c=s[x]-'a',cur=getfail(last,x);
val[x]=1e9;
if(!ch[cur][c]){
int now=newnode(len[cur]+2);
fail[now]=ch[getfail(fail[cur],x)][c];
ch[cur][c]=now;
anc[now]=((fail[now]>1&&len[now]-len[fail[now]]==len[fail[now]]-len[fail[fail[now]]])?anc[fail[now]]:now);
}
last=ch[cur][c];
for(cur=ch[cur][c];cur>1;cur=fail[anc[cur]]){
tval[cur]=val[x-len[anc[cur]]];
tpos[cur]=x-len[anc[cur]];
if(anc[cur]!=cur&&tval[fail[cur]]<tval[cur]) tval[cur]=tval[fail[cur]],tpos[cur]=tpos[fail[cur]];
if(val[x]>tval[cur]+1) val[x]=tval[cur]+1,pos[x]=tpos[cur];
}
}