模板例題:
又稱回文樹,顧名思義,對於乙個字串的回文樹來說,每個節點表示乙個回文串。
例: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...