回文自動機

2021-10-07 11:06:10 字數 2200 閱讀 5897

模板例題:

又稱回文樹,顧名思義,對於乙個字串的回文樹來說,每個節點表示乙個回文串。

例:abbabba

對應節點有:a,b,bb,bab,abba,bbabb

用於統計某字串有多少個回文子串之類問題。

考慮每新增乙個字元對回文樹節點個數的影響

如圖所示,當新增c後,考慮以c為結尾最長的回文串a,假設a未在回文樹**現過,那麼節點樹+1,並且不存在比a短並且未在回文樹**現的情況,所以每加入乙個字元最多新增n個節點,所以回文樹節點數和字串數等價。

由於回文串分奇回文和偶回文,所以先建立兩個根節點0 1

分別表示回文串的根節點和回文串的根節點。

所以轉移函式就為

trans[i]

[c]= j 表示從i節點代表的回文子串兩頭拼接上相同的字元c, 變成了j節點代表的回文子串.

那麼考慮增量轉移,即對於第i個字元,怎麼確定他是由當前回文樹中那個節點轉移得到的?

fail指標(和ac自動機類似的構造思想),每個節點fail含義為,該節點代表的最長非自身回文字尾

定義last表示以i-1為結尾的最長回文字尾的節點,那麼如果要找以i為字尾的最長回文字尾的節點就相當於不斷找滿足s[i-pam[last].len-1]==s[i]的過程,即

while

(s[i-pam[u]

.len-1]

^s[i]

) u=pam[u]

.fail;

所以trans[u][s[i]] 就是我們要找的以i為字尾的最長回文字尾的節點那麼當這個節點沒出現過時,我們就把這個節點加入回文樹中,並且由於還要為接下來的字元建立fail指標,設z為當前節點,fa為z的父親節點,那麼

int w=

getfail

(i,pam[fa]

.fail)

;pam[z]

.fail=pam[w]

.trans[c]

;

並且由於最長非自身回文字尾一定在回文樹**現過,所以不需要考慮pam[z].fail==0的情況(至於為什麼,前面已經說過了)

這樣我們只需不斷新增字元,重複上述過程就能構造出一顆回文樹了

1 求串s字首1~i中本質不同回文串個數(tot-1)

2 求串s內每乙個本質不同回文串出現次數

3 求串s內回文串個數

4 求以下標i結尾的回文串個數

定義一些變數:

len表示當前節點對應回文子串長度

num表示前pam節點(即讀入s[i]之後,s[1,…,i]的最長回文字尾對應節點u)對應回文子串的所有不同回文字尾的個數(包括自身)

sz表示當前節點對應回文子串在主串**現次數(需要處理完整個主串後倒著對faildp)

具體請看模板?

const

int sz =26;

///字符集

const

int maxn =

1e6+6;

struct pam pam[maxn]

;int tot;

char s[maxn]

;void

init()

inline

intnewnode

(int len)

inline

intgetfail

(int i,

int u)

inline

int(

int i,

int u)

pam[u]

.sz++

;return u;

}void

calu()

}}pa;

模板題。

對於每個點,它的貢獻為點編號大於1的祖先個數,和fail鏈節點個數,但很明顯會有重複節點存在,考慮dp,記憶化即可。

建立fail樹後記憶化搜尋

回文自動機

回文自動機,又叫回文樹,是由俄羅斯人 mikhailrubinchik於2014年夏發明的 這是一種比較新的資料結構,在原文中已有詳細介紹與 實現。回文樹其實不是嚴格的樹形結構,因為它有是兩棵樹,分別是偶數長度的回文樹和奇數長度的回文樹,樹中每個節點代表乙個回文串。為了方便,第一棵樹的根是乙個長度為...

回文自動機

乙個節點表示乙個回文串。tot 節點個數,即不同回文串的個數。兩棵樹,節點為0,1,所以最後計數時從2開始 n 新增的字元個數 last 新新增乙個字母後所形成的最長回文串表示的節點 nxt i c 節點i表示的回文串在兩邊新增字元c後變成的回文串編號 兒子 cnt i 節點i表示的本質不同的串的個...

回文自動機

小小總結 別忘了寫上初始化!當字串下標從0 00開始時,pos pospo s初始化為 1 1 1 若從1 11開始,則pos pospo s初始化為0 00 最終的pos pospo s代表最後乙個字元的下標 前者為n 1 n 1n 1,後者為n nn 根據本質不同的回文子串數量不超過 s s s...