回文自动机:从入门到只会打板
寫在前面
如果你會SAMSAMSAM,相信回文自動機不會難懂。
如果你不會,你可以參考我的上一篇文章。
至少回文自動機是治愈系的吧。
作用
回文自動機,也叫回文樹,簡稱PAMPAMPAM實際上它既不是自動機也不是樹
處理回文串的有力工具。可完全代替ManacherManacherManacher,就是會多個字符集。
算法流程
首先一個結論:往一個串末尾加入一個字符,最多會增加一個沒出現過的回文串。
證明:
假設出現了兩個之前沒有出現的,它們都是當前串的后綴。
根據對稱性,把短的關于長的的中心作對稱,得到一個相同的回文串,說明已經出現過了。
矛盾,所以最多出現一個。
這么說,對于字符串SSS,本質不同的回文串最多有∣S∣|S|∣S∣個。
于是可以每個狀態表示一個本質相同的回文串。用lenilen_ileni?表示狀態iii表示的回文串的長度。
考慮到回文串兩邊一樣的,定義轉移ccc表示當前串左右各加上一個ccc
因為分奇回文和偶回文,所以定義兩個初始節點0,10,10,1,其中len[0]=0,len[1]=?1len[0]=0,len[1]=-1len[0]=0,len[1]=?1(讓len[1]=?1len[1]=-1len[1]=?1可以說是這個算法最妙的地方)
000表示偶回文,111表示奇回文。?1-1?1可以看做添加時吞掉一個字符。
類比ACACAC自動機,定義fail[i]fail[i]fail[i]為這個回文串的最長非自己的回文后綴(以下簡稱回文真后綴)。
強行讓fail[0]=fail[1]=1fail[0]=fail[1]=1fail[0]=fail[1]=1
實際上有個隱含條件:1的所有轉移都到0
接下來同樣考慮增量。
當我們插入S[i]S[i]S[i]時,現在我們知道S[i?1]S[i-1]S[i?1]的所有回文后綴,要求S[i]S[i]S[i]的回文后綴。
我們可以從前一個點跳failfailfail,如果到某個點ppp剛好可以前后接上S[i]S[i]S[i],即S[i?len[p]?1]=S[i]S[i-len[p]-1]=S[i]S[i?len[p]?1]=S[i],說明有一個ppp到當前點的轉移。
如果已經有轉移了,說明這個回文串出現過,直接退出。
接下來維護failfailfail。我們發現最長回文真后綴和它本身具有相同的性質。
在之前的基礎上繼續跳就可以了。如果跳到某個ppp有S[i]S[i]S[i]的轉移,說明ch[p][S[i]]ch[p][S[i]]ch[p][S[i]]是個回文后綴,連過去就可以了。
栗子:AABAAABAAABA
插入AAA
此時len[1]=?1len[1]=-1len[1]=?1的優勢就體現出來了,因為剛好是S[i]=S[i]S[i]=S[i]S[i]=S[i]
插入AAA,依次跳到0,10,10,1
插入BBB
插入AAA
實現
第一步插入的時候由于一些玄學問題,ppp的failfailfail可能接到自己身上
解決策略是先把fail算出來,再接到之前的節點后面
剩下的就很容易了
代碼有點古怪,僅供參考
char s[MAXN]; int n; int ch[MAXN][26],fail[MAXN]; int len[MAXN]; int las=1,tot=1; void init() {len[1]=-1;fail[0]=fail[1]=1; } void insert(int i) {int p=las;while (s[i-len[p]-1]!=s[i]) p=fail[p];if (ch[p][s[i]-'a']) return (void)(las=ch[p][s[i]-'a']);int q=fail[p];while (s[i-len[q]-1]!=s[i]) q=fail[q];las=++tot;fail[las]=ch[q][s[i]-'a']; len[ch[p][s[i]-'a']=las]=len[p]+2; }運用
①每個點結尾的回文個數
即failfailfail樹的深度
②本質不同的回文個數
就是狀態數
好像只有這些……
想到再補吧
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的回文自动机:从入门到只会打板的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 后缀自动机:从入门到放弃
- 下一篇: 钱咖怎么下载 钱咖APP下载安装图文教程