1. 程式人生 > >「學習筆記」迴文樹/迴文自動機(Palindromic Tree)

「學習筆記」迴文樹/迴文自動機(Palindromic Tree)

引入

有時候題目要求一些這樣的問題
1. 求以串 s 本質不同的迴文串個數(即長度不同或長度相同且至少有一個字元不相同的字串)
2. 求以位置 i 結尾的迴文串個數。

這時候使用Manacher顯然有點力不從心,我們可以使用一種比較新穎的字串處理工具迴文樹(Palindromic Tree)。

迴文樹的結構

迴文樹其實是由兩棵樹組成的森林,第一棵樹的根節點是

o d d ,第二棵樹的根節點是 e v e n 。每個森林中的節點其實是原串中的一個迴文串。

每個節點儲存以下資訊:

  1. l e n 表示當前節點代表的迴文串的長度。特別的, l e n o
    d d = 1
    , l e n e v e n = 1
  2. f a i l 表示當前節點失配以後可能匹配的最長迴文串(即當前節點的最長迴文字尾),特別的 f a i l o d d = o d d , f a i l e v e n = e v e n
  3. c h 表示當前節點的孩子,其中 c h a 表示在當前字串前後接上字元 a 所形成的新迴文串。

示例:下圖是字串 b a b b a b 的迴文樹(實線表示 c h ,虛線表示 f a i l )

Markdown

其實就是論文1裡的圖啦
不難看出 o d d 的子樹儲存的都是奇數長度的迴文串,而 e v e n 的子樹中儲存的都是偶數長度的迴文串。

迴文樹的構造

迴文樹的構造採用增量構造。
假設我們已經構造串 s 的迴文樹。現在要求出在 s 後新增字元 c 的迴文樹。

定理:以新加入的字元為結尾的,且未在 s 中出現的迴文字串最多有 1 個,且必為新串的最長迴文字尾。

所以只需要求出新串的最長迴文字尾即可。不妨設原串 s 的最長迴文字尾為 s i . . | s | ,那麼只要 s i 1 = c ,則新串的最長迴文字尾一定為 s i 1.. | s | + 1 ,否則轉移到 f a i l s i . . | s | ,繼續之前的操作。

如果新串的最長迴文字尾沒有在迴文樹中,則新建一個節點並找出它的 f a i l ,方法同上面類似。

迴文樹的複雜度

可以證明一個串 s 本質不同的迴文串不超過 | s | 個,所以狀態數為 O ( | s | )

而通過勢能分析可以證明前文所述的方法時間複雜度為 O ( | s | l o g ) (使用平衡樹或c++中的map表示 c h )或 O ( | s | ) (使用陣列表示 c h )。其中 為字符集大小。

迴文樹的一些擴充套件

  1. 從前端插入([HDU5241]Victor and String)
  2. 前後端插入,刪除字元
  3. 可持久化迴文樹

老實說,除了第一個我都不會我還是太菜了。但論文裡有詳細講解。

模板題

[APIO2014]Palindrome

Description

考慮一個只包含小寫字母的字串 s ,定義一個迴文串的出現值為 t s 中的出現次數與其長度的乘積。求 s 的所有迴文字串中的最大出現值。

Solution

首先建出 s 的迴文樹,定義