后綴樹Suffix-Tree的應用
關於字符串的處理,如最長公共子串、回文問題等,用后綴樹可以很好的解決,下面對其應用做一個簡單的介紹。
什么是后綴樹
后綴樹(Suffix tree)是一種樹形數據結構,能快速解決很多關於字符串的問題。后綴樹的概念最早由Weiner 於1973年提出,既而由McCreight 在1976年和Ukkonen在1992年和1995年加以改進完善。
總結起來,它主要可以解決類似如下的一些問題:
- 查找字符串o是否在字符串S中
- 指定字符串T在字符串S中的重復次數
- 字符串S中的最長重復子串
- 兩個字符串S1,S2的最長公共部分
這些問題如何解決,我們最后再談。
結構定義
假設我們現在有一個字符串T = "mississippi",那么我們可以列出它所有的后綴字符串:
1
2
3
4
5
6
7
8
9
10
11
12
|
T1
=
mississippi
T2
=
ississippi
T3
=
ssissippi
T4
=
sissippi
T5
=
issippi
T6
=
ssippi
T7
=
sippi
T8
=
ippi
T9
=
ppi
T10
=
pi
T11
=
i
T12
=
(
empty
)
|
然后對所有的非空后綴做排序,可以得到如下字符串組:
1
2
3
4
5
6
7
8
9
10
11
|
T11
=
i
T8
=
ippi
T5
=
issippi
T2
=
ississippi
T1
=
mississippi
T10
=
pi
T9
=
ppi
T7
=
sippi
T4
=
sissippi
T6
=
ssippi
T3
=
ssissippi
|
可以看出很明顯的樹形規律,我們可以把這棵樹畫出來:
其中每個葉子節點都可以引出一個字符串,圖中一共十個字符串(缺少空串和i字串,為了解決此問題,通常我們會在字符串的末尾加上一個$符號,這樣構造后綴樹的時候就不會出現少串的現象了)
上圖中,每個葉子節點在原字符串的起始位置也可以標出來,即:
后綴樹的應用
現在再來看看本文開頭提到的四個問題,對照上面的圖,我們很容易可以獲得他們的解法:
- 查找字符串o是否在字符串S中
解法:如果S存在於o中,那么S必然是o的某一個后綴的前綴,按照Trie樹搜索前綴的方法,遍歷后綴樹即可。復雜度為O(M),其中M為字符串S的長度。 - 指定字符串T在字符串S中的重復次數
解法:在字符串S后追加$構造包含所有后綴的完整后綴樹,在其中找到T子川,的最后一個節點,該節點擁有的葉子節點個數幾位重復次數,復雜度為O(M),M為T的長度。 - 字符串S中的最長重復子串
解法:遍歷整個后綴樹,找到深度最大的非葉子節點,復雜度為O(N),N為字符串的長度。 - 兩個字符串S1,S2的最長公共部分
解法:分別 為S1、S2追加#、$作為末尾,把他們壓入同一個后綴樹,然后找到最深的非葉子節點,該節點的葉子節點中,既有#又有$。復雜度為構造兩顆后綴樹的復雜度之和,取最大即可max(O(N),O(M)),其中N、M為S1、S2的長度,假設我們以線性時間構造了后綴樹,下位講解構造方法。
最初的構造方法(O(N^2))
按照上文的圖形,我們可以想到一個自然而然的方法構造后綴樹,即遍歷字符串,取得每一個后綴,然后再遍歷這個后綴,依次添加到后綴樹中,這種做法由於要遍歷兩次,復雜度接近於O(N^2),其過程為(假設有字符串S = papua):
改進的構造方法
1995年Ukkonen發明了新的構造算法,可以把后綴樹的構造時間復雜度控制在線性時間內。
后綴樹跟后綴Trie有着一樣的布局, 但它把只有一個兒子的節點給剔除了. 這個過程被稱為路徑壓縮, 這意味着樹上的某些邊將表示一個序列而不是單獨的字符。如上面的papua的后綴樹中,從根節點引出的ua,其實是由兩個節點u、a構成的,但是因為u只有一個兒子節點,所以把他們合並,以壓縮存儲空間。
McCreight最初的構造法是有些缺陷的, 原則上它要按逆序構造, 也就是說字符要從末端開始插入. 如此一來, 便不能作為在線算法, 它變得更加難以應用於實際問題, 如數據壓縮。而且遍歷插入后綴樹也需要N^2兩級的事件。
Ukkonen把原算法作了一些改動, 把它變成了從左往右。對於所給的文本T, Esko Ukkonen的算法是由一棵空樹開始, 逐步構造T的每個前綴的后綴樹. 比如我們構造BANANAS的后綴樹, 先由B開始, 接着是BA, 然后BAN, … . 不斷更新直到構造出BANANAS的后綴樹。如圖:
因此,每次把一個原字符串的前綴追加到后綴樹中,相當於更新該后綴樹,如在BA后加入BAN,相當於只是新增了N字符在末尾,依次類推,我們主要關注后綴樹的更新即可。
具體的更新過程比較復雜,可以參考下面的文章了解。
Suffix tree—后綴樹
l 簡介
后綴樹是一種PAT樹,它描述了給定字符串的所有后綴,許多重要的字符串操作都能夠在后綴樹上快速地實現。
l 定義
一個長度為n的字符串S,它的后綴樹定義為一棵滿足如下條件的樹:
n 從根到樹葉的路徑與S的后綴一一對應。即每條路徑惟一代表了S的一個后綴;
n 每條邊都代表一個非空的字符串;
n 所有內部節點(根節點除外)都有至少兩個子節點。
由於並非所有的字符串都存在這樣的樹,因此S通常使用一個終止符號進行填充(通常使用$)。
l 優點
n 匹配快。對於長度為m的模式串,只需花費至多O(m)的時間進行匹配。
n 空間省。Suffix tree的空間耗費要低於Suffix trie,因為Suffix tree除根節點外不允許其內部節點只含單個子節點,因此它是Suffix trie的壓縮表示。