字尾樹系列三:字尾樹的應用
前面兩篇轉載的字尾樹系列文章已經描述了字尾樹的線性構建演算法。建立字尾樹的O(n)演算法,除了1995年E. Ukkonen大幅簡化的演算法,還有Peter Weiner的73年年度最佳演算法、Edward McCreight1976的改進演算法、Juha Kärkkäinen 和 Peter Sanders2003年進一步簡化的線性演算法,可以根據自己喜好選擇。
轉載的本系列文章應該預設有第三篇的(字尾樹應用),但是沒找到,那就自己總結一下吧。
先擴充套件一下字尾樹的概念:廣義字尾樹(Generalized Suffix Tree)。傳統的字尾樹處理一坨單詞的所有後綴。廣義字尾樹儲存任意多個單詞的所有後綴。注意我們需要區分不同單詞的字尾,所以葉節點用不同的特殊符號與字尾位置配對。
主要應用場景
1. 查詢字串 Pattern 是否在於字串 Text 中(生信中的exact string matching)
解決方案:用 Text 構造字尾樹,按在 Trie 中搜索字串的方法搜尋 Pattern 即可。若 Pattern 在 Text 中,則 Pattern 必然是 Text 的某個字尾的字首。
採用字尾樹的方法,其總的計算複雜度並不劣於KMP等經典方法,可以作為重要的替代方法。此外更重要應用場景如下:
Exact set matching problem(多個子串的情況);
The substring problem for database of patterns(多個母串的情況)。
2. 計算指定字串 Pattern 在字串 Text 中的出現次數
解決方案:用 Text+$
構造字尾樹,搜尋 Pattern 所在節點下的葉節點數目即為重複次數。如果 Pattern 在 Text 中重複了 c 次,則 Text 應有 c 個字尾以 Pattern 為字首。
3. 查詢字串 Text 中的最長重複子串
解決方案:用 Text+$
構造字尾樹,搜尋 字尾樹中最深的非葉節點。從 root 到該節點所經歷過的字串就是最長重複子串。
4. 查詢兩個字串 Text1 和 Text2 的最長公共部分(即Lowest Common Ancestor)
解決方案:連線 Text1+#
$
形成新的字串並構造字尾樹,找到最深的非葉節點,且該節點的葉節點既有 #
也有 $
。 另外,這個可以從兩個推廣到多個字串。
查詢LCA的演算法是O(1)的複雜度,代價是需要對字尾樹做複雜度為O(n)的預處理。
曾記得以前學過樹結構的LCA各種演算法,比如Tarjan演算法(DFS+並查集,離線)、RMQ(時間戳,線上)、樹鏈剖分等,年代久遠,這些東西都記不清楚了T_T。
此外,還有一種二進位制樹檢索LCA的方法,用二進位制的每一位表示路徑方向,0表示左兒子,1表示右兒子。只需將需要比較的兩個節點的二進位制值進行XOR計算,即可得知二者重疊的路徑(根據XOR的定義知,左邊連續的0右多少個,則有多少重疊)。將任意字尾樹mapping到二進位制樹中,即可快速地檢索出LCA。(關於二進位制樹,具體請參考Algorithms on Strings, Trees and Sequences 一書)
5. 查詢給定字串 Text 裡的最長迴文(palindrome in DNA or RNA)
解決方案:將 Text 整體反轉形成新的字串 Text2,例如 “abcdefgfed” => “defgfedcba”。連線 Text+#
+ Text2+$
形成新的字串並構造字尾樹,然後將問題轉變為查詢 Text 和 Text1 的最長公共部分。
注意,DNA或RNA中迴文的定義略有區別。
6. 識別DNA汙染問題
定義:給定一個新測序的DNA序列S1和一個來自可能的汙染物的DNA序列S2, 發現S2中所有出現在S1中且長度超過給定閾值x的子串,如果S2中的某個子串出現在S1中, S1可能已經被汙染了。
解決方案:本質上是LCA問題的變種。
7. All-pairs suffix-prefix matching
定義:給定字串Si和Sj,若Si的字尾與Sj的字首匹配,則稱suffix-prefix match。如果將兩個字串的問題,推廣到多個字串S1,S2,S3…Sk的情況,對於任意其中兩個Si和Sj進行suffix-prefix match,其中最長的suffix-prefix match稱為All-pairs suffix-prefix matching。
解決方案:可以線上性時間內解決,本人暫時未仔細閱讀這個方法,請參考Algorithms on Strings, Trees and Sequences 一書。
8. Circular string linearization
定義:字串S中的任意一個位置切斷,而原先的首位相接,可以得到新的字串。如何選擇這個位置,使得新字串的字典序最小。
解決方案:給定長度為n的字串S,先複製成2倍長度變成SS,構建SS+$
的字尾樹T,從T的根節點開始,每次順著字典序最小的分支走下去,直至深度為n,得到的字串對應的切割方案即為所求。
9. Suffix trees in genome-scale projects
一些基因工程裡已經應用了字尾樹的方法,比如Arabidopsis thaliana(擬南芥)、Yeast(酵母)、Borrelia burgdorferi(博氏疏螺旋體)等
其他應用場景
- Longest common extension;
- Finding all maximal palindrome in linear time;
- Exact matching with wild cards(萬用字元);
- The k-mismatch problem;
- Approximate palindrome and repeats;
- Faster methods for tandem repeats;
- A linear-time solution to the multiple common substring problem.
空間優化
構建Directed Acyclic Word Graph,簡稱DAWG。具體請檢視相關資料(比如Algorithms on Strings, Trees and Sequences 一書中就有章節講述:Building a smaller directed graph for exact matching)。
Algorithms on Strings, Trees and Sequences 一書中還有幾個空間優化的例子:
A reverse role for suffix trees, and major space reduction;
Space-efficient longest common substring algorithm;
Suffix arrays——more space reduction.
其他
Algorithms on Strings, Trees and Sequences 一書中還提到了一些相關的知識。
1. Boyer-Moore如何解決exact set matching problem;
2. Ziv-Lempel 資料壓縮;
3. Minimum length encoding of DNA.
推薦的參考資料
關於字尾樹的講解,還有一些不錯的文章:
1. 字尾樹 (該作者不允許轉載。。)
2. 祥林嫂精神恍惚痛苦呼喚之關於Suffix Tree[字尾樹] (圖文可見 此部落格 )
其他參考資料:
- Pattern Searching | Set 8 (Suffix Tree Introduction)
- 字尾樹的構造方法-Ukkonen詳解
- Ukkonen’s Suffix Tree Construction – Part 1
- Suffix Trees
- Compressed Trie
- Pattern Searching using a Trie of all Suffixes
- Algorithms on Strings, Trees, and Sequences
- C# Suffix tree implementation based on Ukkonen’s algorithm
- Ukkonen’s suffix tree algorithm in plain English?
- Ukkonen 的字尾樹演算法的清晰解釋
- Fast String Searching With Suffix Trees
- Esko Ukkonen’s Paper: On–line construction of suffix trees
- Graphviz - Graph Visualization Software
- a suffix tree algorithm for .NET written in C#