省選演算法學習-迴文自動機 && 迴文樹
前置知識
首先你得會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];
}
}