回文這個東西,有一些不錯的演算法。比如馬拉車演算法,就是一種非常優秀的演算法。
我也學過一篇有關馬拉車的文章,非常的簡單易懂。但我們今天講的是比馬拉車更強的演算法——回文自動機。
回文自動機和ac自動機有一些類似的地方,所以有興趣的同學可以看這篇文章來了解 ac自動機
好了,在開始今天的正文之前,我們得先定義一些陣列,以便更好的了解回文自動機。
fail[x]:x失配後跳轉到的不等於自身的最長字尾回文子串。(這個可能有點難看懂,但是參考ac自動機fail)定義完這些陣列後,我們就開始構建字尾自動機了。len[x]:以x為結尾的最長回文子串的長度。
cnt[x]:與以x結尾的最長回文子串相同的子串的個數
son[x][c]:編號為x的節點表示的回文串在兩邊新增字元c以後變成的回文串的編號
s[x]:第x次新增的字元(一開始設s[0] = -1,也可以是任意乙個在串s中不會出現的字元)。
首先我們建兩個空節點0和1,其中fa
il[0
]=1,
len[
1]=−
1 fai
l[0]
=1,l
en[1
]=−1
,這樣的設定對後面有用。
然後我們讀入乙個字元,找出以它為結尾的最長回文子串的長度。這段找的**如下:
ll get_fail(ll p,ll x)
那麼節點x的最長回文子串的長度為函式le
n[p]
+2l en
[p]+
2。因為我們看某個回文子串的最左端是否和新加入的字元一樣,如果一樣那就是我們要求的回文子串,如果不一樣,我們就跳轉到當前回文串的最長字尾回文子串,繼續匹配。是不是有點類似與ac自動機?
舉個例子,比如當前串為cbbabb,那麼最長的回文子串為bbabb,我們要加入字元a,那麼a先與bbabb左邊的字元(c)比較,發現不一樣,於是跳轉到bbabb的最長的字尾回文子串,也就是bb,繼續匹配。發現bb左邊的字元為a,和我們要加入的字元一樣,所以新加入的len就是le
n(bb
)+2=
4 len
(bb)
+2=4
,也就是子串abba。
然後我們再求出它的fail,也就是當前串abba的最長字尾回文子串,那麼我們拿bb繼續和a匹配,很遺憾不能匹配,所以會一路跳轉到0,而fa
il[0
]=1 fai
l[0]
=1
,所以到了點1,而le
n[1]
=−1 len
[1]=
−1
,帶到我們的ge
t get
_fail
f ai
l裡發現是s[
x−(−
1)−1
]=s[
x]s [x
−(−1
)−1]
=s[x
],也就意味著s[
x]=s
[x] s[x
]=s[
x]
所以它的最長字尾回文子串就是它自己b,所以我們把fail連到找到的點即可。fail[now]=son[get_fail(fail[cur],i)][s[i]-'a'];//cur=get_fail(last,i),s[i]是新加入的字元
#include
#define maxn 300010
#define ll long long
using
namespace
std;
ll read()
char s[maxn];
ll fail[maxn],son[maxn][26],len[maxn],cnt[maxn];
ll tot,last,cur,ans;
ll newnode(ll x)
ll get_fail(ll p,ll x)
int main()
cnt[last=son[cur][s[i]-'a']]++;
}for(i=tot-1;i>=0;i--)
return
0;}
給大家推薦這篇部落格,裡面有詳細的例子和** 初學回文自動機
回文自動機,據說是解決回文問題的利器。最近剛好遇到一道回文問題,加上正好閒著沒事幹,就來學了學。感覺板子還是非常簡潔的,容易記憶。與一般自動機類似,定義乙個節點的資訊。一般包括表示長度 fail 指標 後繼節點,當然視具體題目還要維護一些特殊資訊。但也要注意回文自動機與一般自動機的區別,就是它的後繼...
回文自動機 初學
今天學習了一下回文自動機pam,發現比字尾自動機sam簡單好多啊。clj那個sam的ppt講的我頭昏腦漲的 叫你弱叫你弱 簡單寫一下免得自己忘記。幾個陣列 1.像各種自動機一樣,用ch x c 表示狀態x後新增字母c之後轉移到的狀態。2.同樣,用sum x 表示狀態x所含的回文串的個數 和sam一樣...
回文自動機
回文自動機,又叫回文樹,是由俄羅斯人 mikhailrubinchik於2014年夏發明的 這是一種比較新的資料結構,在原文中已有詳細介紹與 實現。回文樹其實不是嚴格的樹形結構,因為它有是兩棵樹,分別是偶數長度的回文樹和奇數長度的回文樹,樹中每個節點代表乙個回文串。為了方便,第一棵樹的根是乙個長度為...