suffix tree
文章出處:http://www.cnblogs.com/snowberg/archive/2011/10/21/2468588.html
3???What is a Suffix Tree
Suffix tree, 或?后綴樹?,是一種相當神奇的數據結構,它包含了字符串中的大量信息,能夠用于解決很多復雜的字符串問題 —— 事實上,基本上目前為止俺遇到過的所有與字符串有關的問題都可以通過它解決。
我們以字符串?MISSISSIPPI?為例,先列出它所有的后綴:
1. MISSISSIPPI 2. ISSISSIPPI 3. SSISSIPPI 4. SISSIPPI 5. ISSIPPI 6. SSIPPI 7. SIPPI 8. IPPI 9. PPI A. PI B. I將它們分別插入到?字典樹?中我們可以得到:
BTW, 樹中的?$?符號表示字符串結尾 —— 這里?I?是字符串?MISSISSIPPI?的結尾,而?I?又在其中間出現了,所以有必要使用一個特殊的符號標識出來,同理在后綴樹中也需要有這樣的一個符號;雖然 C 語言中有'\0'?,但這里使用?$?符號似乎屬于約定俗成的事情了,咱這次也就從了它吧。
這樣的一棵字典樹已經具備了后綴樹的功能 ——
比如說吧,你從根節點開始順著這個字典樹的隨便一個樹枝走 n 步,然后數一下再往下有幾片葉子,就知道已經走過的這 n 個字符組成的字符串在原字符串中一共出現過多少次了 —— 由于你可以把某一節點下方的葉節點數目保存在該節點中,所以這個算法實際上是 O(n) 的,怎么樣?
只是,你也看出來了,它的空間復雜度相當之高,實際上基本上沒有誰會用這樣一個數據結構。
于是,?把所有最近兩個有多個分支的節點之間的部分(它們每個節點各自僅有一個分支)合并起來?(參考:Patricia Trie?或曰?Radix Tree),我們可以得到:
這就是一棵后綴樹了。
順便提一句,雖然邏輯結構如上,但實際的數據結構中存儲的東西大致是這樣的:
這樣,每一條邊占用的空間為常數大小,于是后綴樹 Tree(T) 的空間復雜度為 O(|T|),|T| 為字符串 T 的長度。?因為?:
- 從樹根到樹葉的每一條路徑組成了一個后綴
- 所以樹葉的數量等于后綴的數量
- 每個非葉子節點都有至少兩條樹枝
- 所以非葉節點的數量小于葉節點的數量
- 除去根節點,節點數等于邊數
- 所以,后綴樹的空間復雜度為: O(|T| + size(edge labels))?[1]
后綴樹和?后綴 Trie?一樣包含了整個字符串中所有子串的信息,又僅占用 O(|T|) 空間,而且 —— 后面會說到,它可以在 O(|T|) 時間內構造出來,這些特性使它成為了一個理論分析以及實際應用上都非常重要的數據結構。
4???What Can It Do
后綴樹的用途是如此之廣以至于 Dan Gusfield 在他的書中?[2]?花了七十多頁來介紹,我在這里先挑幾個自己看來比較好玩的例子說說,以期待激發讀者的興趣。
4.1???Longest Common Substring
Longest common substring?- 即?最長公共子串?,是指在兩個字符串中都出現了的最長子串 —— 比如,superiorcalifornialives?和?sealiver?的最長公共子串為?alive?。
這是個很經典的例子,據說 1970 年,后綴樹的概念還沒有被提出來的時候,Knuth 爺爺曾無比確信找出兩個字符串最長公共子串的線性算法是不存在的?[2]?;然而,在知道后綴樹的概念之后,這個算法也顯而易見了:
?
4.2???Exact String Matching
當字符串 T 已知且固定不變,P 改變時:
我們可以用 O(|T|) 時間預處理 T,然后在 O(|P|) 時間內回答出 “P 在 T 中出現了幾次?” 或是 “P 在 T 的哪些地方出現過?” 之類的問題:
預處理 T, 得到它的后綴樹 —— 也可以把多個字符串 T1, T2 ... 放入同一個后綴樹,然后查找 P 時只需要從這個后綴樹的根開始順著 P 往下走,然后讀出保存在節點中的信息 (下方的樹葉數量、出現地方 &etc.) 就行了。
而這也就意味著你可以把很大的文檔進行?索引?,然后快速的進行搜索。
當字符串 P 已知且固定不變時:
我們也可以用 O(|P|) 時間預處理模式串 P,然后用 O(|T|) 時間在 T 中找出 P —— 時間、空間復雜度的上界就和 KMP 算法或是 Boyer-Moore 算法一樣。
這個可能看不明顯,但是,這么說吧,KMP 算法中 Next 數組保存的所有信息、Boyer-Moore 算法中 Bad-Charactor 和 Good-Suffix 表的信息后綴樹中都有。 由于實際操作上比較復雜,這里就不展開了,讀者請自行參考?[2]?或者耐心等待?If?(嗯,也就是本人) 心血來潮的那一天。
當 P 和 T 都已知的時候:
很明顯,我們可以在 O(|T| + |P|) 時間內找出 P 在 T 中的出現,不說了。
4.3???Ziv-Lempel Compression
嗯,常說的 LZ77 指的就是 Lempel Ziv 1977。
Ziv-Lempel 壓縮的主要思想是,將前方出現過的子串使用?(位置, 長度) 對?表示出來,例如aaaaaaaaaaaaaaaa?就可以表示為?a(1,1)(1,2)(1,4)(1,8)?:
a(1,1) == aa aa(1,2) == aaaa aaaa(1,4) == aaaaaaaa aaaaaaaa(1,8) == aaaaaaaaaaaaaaaa真正的 LZ 編碼方式和這里說的有點區別,但思想是一樣的。
有了后綴樹,LZ 壓縮也簡單了:
以上查找 (位置, 長度) 序列的算法時間復雜度是 O(長度),于是壓縮算法整體復雜度就是 O(|T|) 。
—— BTW,后面會說到,根據 Ukkonen 的算法?[3]?,后綴樹本身可以 "On-line" 構造,即,每次加入一個字符,得到當前的后綴樹;這樣, Ziv-Lempel 壓縮也可以僅用一次自左向右的掃描完成。
5???Few New Concepts
在繼續講如何構造后綴樹之前,為了后面表述的方便,再提幾個簡單的概念:
Explicit Node (顯式節點)?:6???How To Build It
目前我只弄懂了 Ukkonen 的算法?[3]?,所以下面也只說這一種算法:
6.1???Native Approach
首先,我們觀察發現,若?S?是一個非空字符串,?c?是一個字符,則將?c?加到?S?的每個后綴(包括空后綴)后面,我們可以得到?Sc?的所有后綴。
Suffixes(a):aSuffixes(ab):abbSuffixes(abc):abcbccSuffixes(abca):abcabcacaa...很顯然,可以將這樣的原理用于構建后綴樹:假設 T[0..i-1] 的后綴樹已經建好了,那么在 T[0..i-1] 的每個后綴 T[0..i-1], T[1..i-1] .. T[j..i-1] .. T[i-1..i-1] 的后面加上字符 T[i] 就可以得到 T[0..i] 的后綴樹了。
具體到 “將 T[i] 加到后綴 T[j..i-1]” 的這個過程,則有可能遇到三種情況:
這種情況下,只需要把 T[i] 加到該樹葉的邊上即可。
這種情況下,在 T[j..i-1] 的后面加上樹葉 T[i]
(可能需要把隱式節點變換成顯式節點)。
這種情況下,我們什么都不用做。
于是,如果不加任何形式的優化,那么 Ukkonen 的算法就是這么回事:
令 n = |T|; 在 T 尾部加上終結符 $。 for i from 0 to n: // n+1 個階段(包括加上終結符)for j from 0 to i-1: // 每個階段 i 次擴展從后綴樹的根順著 T[ j..i-1 ] 走到節點 v (顯式/隱式)如果 v 下方節點中存在第一個字符為 T[i] 的,則什么都不用做。否則如果 v 是隱式節點,那么:將 v 改成顯式節點。在 v 下增加葉節點,從 v 到新節點的邊標記為 T[i]。若 v 是內部節點或根節點:在 v 下增加葉節點,從 v 到新節點的邊標記為 T[i]。否則: (只能是葉節點了):將 T[i] 加到邊的尾部。具體的,這里給出構建?MISSISSIPPI$?的后綴樹的全過程,如下:
6.2???Speed Up!
上面的原生態算法雖通俗易懂老少皆宜,但其時間復雜度相當之高 ( O(|T|3) ), 想要有實用價值必須得提高它的效率 —— 而正如前面提到過的,其實后綴樹可以在 O(|T|) 時間內構造;這個 O(|T|) 的奇妙算法和上面的算法主體流程其實一樣,下面就要介紹加速妙方了,請坐穩。
6.2.1???Stop When?T[j..i]?Exists
首先說最容易理解的:
很顯然,如果后綴樹 STree(T[0..i-1]) 中已經存在 T[j..i] 了,那么 T[j+1 .. i] 肯定也在其中,?因為?:
于是乎,碰到 T[j..i] 存在的情況,就不必繼續傻乎乎的在樹中查找 T[j+1 .. i-1], T[j+2 .. i-1] ... 了。
6.2.2???Opened Leaves
若知道了字符串的長度?l?,又知道了樹葉的起點?d?,就可以知道這個樹葉的邊的長度為?l?-?d?—— 所以首先,不記錄樹葉的邊長并不影響功能。
而如果直接把所有的樹葉都標記為開放的,做法例如令其長度為無窮大 —— 這樣有一個好處:在對樹葉的擴展中就什么都不用做了。
什么時候可以利用這個小技巧呢?我們看看將 T[i] 加入到 STree(T[0 .. i-1]) 的過程:
-
首先, T[0 .. i-1] 肯定停在樹葉上。
-
假設 STree(T[0 .. i-1]) 有 n 片樹葉,那么 T[1 .. i-1] T[2 .. i-1], T[n-1 .. i-1] 也都會停在樹葉上,因為:
對于任何 j < i-1 如果 T[j .. i-1] 不在樹葉上,那么 T[j+1 .. i-1] 更不可能在樹葉上;
這又是因為, T[j+1 .. i-1] 是 T[j .. i-1] 的后綴,若 T[0 .. i-1] 有子串 S = T[j .. i-1]+'c' (因此 T[j .. i-1] 不在樹葉上),那么 T[0 .. i-1] 肯定也有子串 S[1:] = T[j+1 .. i-1]+'c' (因此 T[j+1 .. i-1] 也不在樹葉上)。
于是如果 STree(T[0 .. i-1]) 有 n 片樹葉,那一定就對應著前 n 個后綴。
所以,已經有了 STree(T[0 .. i-1]) 的話,不必沿著 T[0 .. i-1], T[1 .. i-1] .. T[i-1 .. i-1] 所有這些路徑將 T[i] 加入到后綴樹;而只需要從 n 開始沿著路徑 T[n .. i-1], T[n+1 .. i-1] .. T[i-1 .. i-1] 加入 T[i] 。
6.2.3???Count & Skip
再看從后綴樹的根開始查找路徑 T[j .. i-1] 的過程,不難發現:
某一條邊上進行匹配時,不必一個字符一個字符的比較,而只需要比較第一個字符,跳過剩下來的部分、直接去下一個節點,因為:
6.2.4???Suffix Link
好了,接下來要講最重要也是最難理解的話題:后綴鏈接了。
6.2.4.1???What is It
用漢語解釋 Suffix link 的話,它就是某種顯式節點之間的鏈接,其目的是簡化順著 T[j .. i-1] 找到下一次擴展的地方的過程。
根據?Count & Skip?的技巧,從根節點開始找到擴展點的算法復雜度已經只與下方的分支數有關了,如果能直接從某個很接近擴展點的顯式節點開始查找,那么顯然能夠提高效率。
很巧的,我們發現,如果有某個內部節點的路徑為?aS,其中?a?是一個字符、?S?是一個(可能為空的)字符串,那么肯定也會有一個路徑為?S?的內部節點、即使沒有,也會在下次擴展的時候產生。
因為: 如果?aS?停在一個內部節點上面,也就是?aS?后有分支,那么當前的 T[0 .. i-1] 肯定有子串?aS+c 以及aS+d ( c 和 d 是不同的兩個字符) 這兩個不同的子串,于是肯定也有?S+c 以及?S+d 兩個子串了。至于“下次擴展時產生”的這種情況,則發生在已經有?aS+c 、?S+c ,剛加入?aS+d (于是產生了新的內部節點),正要加入?S+d 的時候。
這樣,我們將節點?aS?指向?S?,并把這樣的一個指向關系稱為?aS?的?后綴鏈接?。
見圖中紅色箭頭:
6.2.4.2???How It Works
后綴鏈接有什么用呢?
前面似乎提示過了,在?aS?后加上了 d 之后,下一個步驟便是在?S?之后加上 d ;于是同樣,在?aSxy?后加上 z (步驟一)之后,下一個步驟是在?Sxy?后加上 z (步驟二)。如果?aS?停在了內部節點上,那么?S?也停在內部節點上,那么從步驟一到步驟二,通過后綴鏈接,可以直接找到路徑為?S?的這個內部節點;然后通過Count & Skip?技巧,可以直接定位到?xy?后方的擴展點。
參考圖中?A?節點到?B?節點的藍色路徑:
這里用?MISSISSIPPI?的例子來講后綴鏈接似乎不太合適,參考它的構造過程圖示,第九步之前樹中一直只有一個內部節點,第九步沒有利用后綴鏈接加速,而第九步之后,根據?Opened Leaves?技巧,只需要對?P?開頭的那一條邊進行擴展……但這個例子絕對不意味著后綴鏈接沒有用。
嗯,比如說,作為一個正面的案例,構建?MISSISSIPPIMISSIA?的后綴樹的第 17 階段(加入?'A'?),第 13、14、15 次擴展便是順著后綴鏈接進行的(?ISSI?->?SSI?->?SI?->?I?->?ROOT?)。
6.2.4.3???How To Maintain It
那么如何建立和維護后綴鏈接呢?
依然根據上面提到的原理,我們發現:
在某一階段中,前一次擴展訪問到(或者建立)的內部節點到本次擴展訪問到的地方要么已經有了一條后綴鏈接,要么就應該建立一條后綴鏈接。
因此,具體實現起來,我們可以保存一個最后訪問的內部節點的指針?p?,訪問到新的內部節點?node?的時候,將?p?的后綴鏈接指向?node?,再將?p?設為?node?即可。
6.3???Why O(n)
至于為什么這個算法是線性的,分析起來倒是不難:
設 n = |T|。
- 假設節點 A 的后綴鏈接指向節點 B ,A 的路徑為?pa?, B 的路徑為?pb?, A 上方有?x?個節點, B 上方有?y?個節點,則有?:
- x?<=?y?+ 1 (因為?pb?是?pa?的后綴,因此除了第一個字符之后的分支,?pa?路徑上的其他分支?pb路徑上必然也有,反之則不然)。
- 因此,再看每次顯式擴展的過程:從當前位置向下找到擴展點;完成擴展; 向上一個節點;跟隨后綴鏈接來到下一個位置。其中除了“從當前位置向下找到擴展點”這一步之外,剩下的都只需要常數時間。
- 而“向下”查找一次之后,跟隨后綴鏈接來到新的一個節點時最多只能使上方的節點數減少 2。
- 后綴樹整體的深度不超過?n?,而總共又只有?n?次顯式擴展,因此節點的訪問次數為 O(n)
6.4???C++ Code
最后,以下代碼是我的實現,使用 boost license 以及 GPL v3 發布,需要用的話請自由選擇~
使用模板一方面是因為字符的確有不同的類型,另一方面(更主要的)是方便把實現和定義放在同一個文件里,以減少鏈接的工作(我懶嘛),用 C 的童鞋,對不住了。
本人對下面程序的任何 bug 引起的任何后果不負任何責任,但是如果發現有 bug 請您務必與俺聯系。
/** File: suffixtree.h* Author: If* Created on Sep/14th/2010, 14:02** License: GPL v3 / Boost Software License 1.0*//**update: Oct/17th/2010:Record belongTo information on leaves only,other works were left to `dfs`, `bfs` and `eachLeaf`function.*update: Oct/17th/2010:Add `eachLeaf` function, for it's quite useful*//* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following:The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *//* Copyright (C) 2010 IfThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. *//* following code may cause permanent brain damage, read at yourown risk. */#ifndef SUFFIXTREE_H #define SUFFIXTREE_H// #define HAS_BOOST#include <functional> #include <algorithm> #include <stdexcept> #include <iterator> #include <iostream> #include <vector> #include <string> #include <stack> #include <queue>#include <assert.h> #include <stdio.h>#ifdef HAS_BOOST #include <boost/pool/object_pool.hpp> #endifusing std::vector; using std::basic_string;#ifdef DEBUG_STREE FILE* __streeLogFile=stderr; #define dbg(...) \ do{ \ fprintf(__streeLogFile, __VA_ARGS__); \ fflush(__streeLogFile); \ } while(0) #define dbg_puts(str, len) \ do{ \ for(int i=0;i<len;++i) \ fputc(str[i], __streeLogFile); \ fflush(__streeLogFile); \ } while(0) #else #define dbg(...) /*nothing at all*/ #define dbg_puts(s, l) /*nothing at all*/ #endiftemplate <class CharT, class Additional=void*> struct Node {// Important members:CharT const* str; // since edge-node has one-to-one correspondenceint len; // there is no need to separate them.int startAt; // for the use of presenting an implicit node.// it will then be the position of first different// charactor.Node * link; // suffix link.Node * parent;vector<Node*> branches;int belongTo; // which string does this leaf belongs to.// *only recorded on leaves*// Misc members:int leafNum; // leaf number ( nth suffix ).int depth; // no need to explain.Additional info; // some more information you need, depending on// your application.Node():str(0),len(0),startAt(0),link(0),parent(0),branches(),belongTo(-1),leafNum(0),depth(0),info(){ }Node(Node const& other):str(other.str),len(other.len),startAt(other.startAt),link(other.link),parent(other.parent),branches(other.branches),belongTo(other.belongTo),leafNum(other.leafNum),depth(other.depth),info(other.info){ }~Node() { }/*static inline int matchFirst(Node const& node, CharT const val){return node.str[0]==val? node.len : 0;}static inline bool ptrMatchFirst(Node const* node, CharT const val){return node->str[0]==val? node->len : 0;}*/struct MatchFirst {CharT val;MatchFirst(CharT v):val(v){ }inline bool operator()(Node const* node) const {return node->str[0]==val;}}; };template <class CharT=char, class T=void*> class SuffixTree { protected:typedef Node<CharT,T> NodeT;typedef typename vector<NodeT*>::iteratorNodeIterator; #ifdef HAS_BOOSTboost::object_pool<NodeT>allNodes_; #endifNodeT* root_;vector<NodeT*> leaves_;vector<NodeT*> allLeaves_;int nLeaves_;vector<basic_string<CharT> >strings_;NodeT* latestInternalNode_;NodeT* currentPos_;int phase_;int extension_;protected:NodeT* allocNode() { #ifdef HAS_BOOSTreturn allNodes_.malloc(); #elsereturn new NodeT; #endif}void freeNode(NodeT *& node) { #ifdef HAS_BOOSTallNodes_.free(node); #elsedelete node; #endifnode = 0;}/*** following str[0:len], and then add str[len] to current tree.** @return true if undone, false if str[0..len] already exists - which means* extension process is done.*/bool extend (CharT const* str, int pos);// follow path "str" from node down.NodeT* jumpDown(CharT const* str, int len, NodeT* node, bool fromFront=0);/*** split one edge from its startAt position.** @param edge: a node with startAt!=0* @param leafLabel: label of the newly created leaf, with length==1* @return newly created leaf.*/NodeT* splitEdge(NodeT* edge, CharT const* leafLabel);void putEnd() {int p = phase_+1;int const nth = strings_.size()-1;for(NodeIterator itr=leaves_.begin(),end=leaves_.end(); itr!=end; ++itr){(*itr)->len = --p - (*itr)->depth;allLeaves_.push_back(*itr);}}#ifndef HAS_BOOSTinline void destroy(NodeT* node) {for(NodeIterator itr=node->branches.begin(), end=node->branches.end();itr!=end; ++itr)destroy(*itr);freeNode(node);} #endifprivate:SuffixTree(SuffixTree<CharT,T> const&); // complicated while useless,// i've got no interest.public:inline SuffixTree();inline SuffixTree(basic_string<CharT> const&);#ifdef HAS_BOOST~SuffixTree() { /*nothing to do*/ } #else~SuffixTree() { destroy(root_); } #endifvoid addString(CharT const * str, int len);void addString(basic_string<CharT> const& str) {addString(str.c_str(), str.length()); // to maintain a copy}NodeT const* root() {return root_;} /*void deleteString(CharT const * str, int len);void deleteString(basic_string<CharT> const& str) {deleteString(str.c_str(), str.length());} */// will call f(node) on each node of this tree, including root.template <class Function>void dfs(Function f) {std::stack<NodeT*> todo;todo.push(root_);while(!todo.empty()) {NodeT* node = todo.top();todo.pop();for(NodeIterator itr=node->branches.begin(),end=node->branches.end();itr!=end;++itr) {todo.push(*itr);}f(*node);}}// will call f(node) on each node of this tree, including root.template <class Function>void bfs(Function f) {std::queue<NodeT*> todo;todo.push(root_);while(!todo.empty()) {NodeT* node = todo.front();todo.pop();for(NodeIterator itr=node->branches.begin(),end=node->branches.end();itr!=end;++itr) {todo.push(*itr);}f(*node);}}template <class Function>void eachLeaf(Function f) {for(NodeIterator itr=allLeaves_.begin(), end=allLeaves_.end();itr!=end; ++itr) {f(**itr);}}// human-readable json-like format outputvoid print(std::basic_ostream<CharT> * stream);// machine-readable dot format output, can be used to// generate graph using Graphvizvoid dot(std::basic_ostream<CharT> * stream); };template <class CharT, class T> SuffixTree<CharT,T>::SuffixTree():root_(0),leaves_(),nLeaves_(0),strings_(),latestInternalNode_(0),currentPos_(0),phase_(0),extension_(0) {root_ = allocNode();root_->parent = root_; // for convenienceroot_->link = root_;root_->len = 0;root_->str = 0;root_->startAt = 0;currentPos_ = root_; }template <class CharT, class T> SuffixTree<CharT,T>::SuffixTree(basic_string<CharT> const& str):root_(0),leaves_(),nLeaves_(0),strings_(),latestInternalNode_(0),currentPos_(0),phase_(0),extension_(0) {root_ = allocNode();root_->parent = root_; // for convenienceroot_->link = root_;root_->len = 0;root_->str = 0;root_->startAt = 0;currentPos_ = root_;this->addString(str); }template <class CharT, class T> void SuffixTree<CharT,T>::addString(CharT const* str, int len) {strings_.push_back(basic_string<CharT>(str, str+len));int whichString = strings_.size()-1;CharT const* cstr = strings_.back().c_str();dbg("adding string \"%s\" to suffix tree ... \n", cstr);root_->len = 0;root_->depth = 0;leaves_ = vector<NodeT*>();nLeaves_=0;currentPos_=root_;for(phase_=0; phase_<=len; ++phase_) { // there's a '\0' behindbool undone = true;latestInternalNode_ = 0;for(int j=nLeaves_; j <= phase_ && undone; ++j) {extension_ = j;undone = extend( cstr+extension_, phase_-extension_ );}}putEnd(); }template <class CharT, class T> typename SuffixTree<CharT,T>::NodeT* SuffixTree<CharT,T>:: jumpDown(CharT const* str, int len, NodeT* node, bool fromFront) {dbg("# jumping down path len=%d str=\"", len);dbg_puts(str,len);dbg("\" from node %08x ... \n", node);int i=0;if(fromFront) {if (node!=root_ && str[0]!=node->str[0]) {dbg("!!! cannot find such path !!!\n");throw(std::runtime_error("path not found"));}i = node->len != -1?node->len:phase_ - node->depth;}for(; i<len; ) {NodeIterator itr =std::find_if( node->branches.begin(),node->branches.end(),typename NodeT::MatchFirst(str[i]) );if(itr == node->branches.end()) {dbg("!!! cannot find such path !!!\n");throw(std::runtime_error("path not found"));} else {dbg("> going into node %08x which begins with \'%c\', len=%d,"" depth=%d\n",*itr, (*itr)->str[0], (*itr)->len, (*itr)->depth );node = *itr;if( node->len != -1 ) // internal nodei += node->len;else // leafi += phase_ - node->depth;}}if( i != len ) { // implicit nodeif(node->len != -1)node->startAt = node->len - (i-len);elsenode->startAt = phase_ - node->depth - (i-len);}dbg("< done.\n");return node; }template <class CharT, class T> typename SuffixTree<CharT,T>::NodeT* SuffixTree<CharT,T>::splitEdge(NodeT* node, CharT const* leafLabel /*len==-1*/) {/*** \* # <-- startAt* \* node** => => => => => => =>** \* \* newInternal* / \* / newLeaf (return value)* node**/dbg("X spliting edge %08x \n", node);NodeT* newLeaf = allocNode();NodeT* newInternal = allocNode();// newInternal->belongTo = node->belongTo;newInternal->startAt = 0;newInternal->len = node->startAt;newInternal->str = node->str;newInternal->parent = node->parent;newInternal->link = root_;newInternal->depth = node->depth;newInternal->branches.push_back(node);newInternal->branches.push_back(newLeaf);*(std::find(newInternal->parent->branches.begin(),newInternal->parent->branches.end(),node)) = newInternal;node->depth += node->startAt;node->str += node->startAt;node->parent = newInternal;if(node->len != -1) {node->len -= node->startAt;}node->startAt = 0;newLeaf->str = leafLabel;newLeaf->depth = node->depth;newLeaf->link = root_;newLeaf->len = -1;newLeaf->parent = newInternal;dbg(" new internal node = %08x ; \t new leaf = %08x\n",newInternal, newLeaf);return newLeaf; }template <class CharT, class T> bool SuffixTree<CharT,T>::extend(CharT const* str, int pos) {dbg("{{{ in phase %d, extension %d;\n", phase_, extension_);dbg(" adding \'%c\' to suffix tree ... \n", str[pos]);int const whichString = strings_.size()-1;int skiped = currentPos_->depth;NodeT *node = jumpDown(str+skiped, pos-skiped, currentPos_, true);if (node->startAt != 0) { // implicit nodedbg(" got implicit node %08x starts at %d\n", node, node->startAt);if(node->str[node->startAt] != str[pos]) {NodeT *newLeaf = splitEdge( node, str+pos );newLeaf->leafNum = extension_;newLeaf->belongTo=whichString;node = newLeaf->parent;if(latestInternalNode_) {latestInternalNode_->link = node;dbg("-> creating suffix link from %08x to %08x\n",latestInternalNode_,node);}latestInternalNode_ = node;currentPos_ = node->parent;leaves_.push_back(newLeaf);++nLeaves_;} else {dbg(" suffix already exists.\n}}} done.\n");node->startAt = 0;return false; // suffix already exists, all done.}} else { // explicit nodedbg(" got explicit node\n");if(latestInternalNode_) {latestInternalNode_->link = node;dbg("-> creating suffix link from %08x to %08x\n",latestInternalNode_,node);}latestInternalNode_ = node;if( std::find_if( node->branches.begin(),node->branches.end(),typename NodeT::MatchFirst(str[pos]) )!= node->branches.end()) {dbg(" suffix already exists.\n}}} done.\n");currentPos_ = node;return false; // all done.} else if(node->branches.empty() && node!=root_) {// a leaf created in the past, now it should be exdended.node->belongTo=whichString;node->str = str+pos-node->len;node->len = -1;currentPos_ = node->parent;node->leafNum = extension_;leaves_.push_back(node);++nLeaves_;} else {// add a leaf to existing explicit node.NodeT *newLeaf = allocNode();dbg(" adding new leaf %08x to node %08x.\n", newLeaf, node);newLeaf->parent = node;newLeaf->link = root_;newLeaf->str = str+pos;newLeaf->len = -1; // leafnewLeaf->belongTo=whichString;newLeaf->depth = node->depth + node->len;node->branches.push_back(newLeaf);currentPos_ = node;newLeaf->leafNum = extension_;leaves_.push_back(newLeaf);++nLeaves_;}}dbg("-> following suffix link from %08x to %08x ...\n",currentPos_,currentPos_->link );currentPos_ = currentPos_->link;dbg("}}} done.\n");return true; }static inline char* x08 (char* buf, void const * ptr) {sprintf(buf, "%08x", ptr);return buf; }template <class CharT, class T> static void __printSuffixTreeNode(Node<CharT,T>* node,int margin_left,int padding_left,std::basic_ostream<CharT> * stream) {int i,I;char addr[16];if( node->branches.empty()) {for(i=0; i<margin_left; ++i)*stream<<' ';*stream << '\"';for(i=0; i<node->len; ++i)*stream<< node->str[i];*stream << "\" " << x08(addr, node)<< "[" << addr << "] ";*stream << "\n";} else {for(i=0; i<margin_left; ++i)*stream<<' ';*stream << '\"';for(i=0; i<node->len; ++i)*stream<< node->str[i];*stream << "\" " << x08(addr, node)<< "[" << addr << "] ";*stream << ": {\n";for(i=0, I=node->branches.size(); i<I; ++i) {__printSuffixTreeNode(node->branches[i],margin_left+padding_left,padding_left, stream);}for(i=0; i<margin_left; ++i)*stream<<' ';*stream << "} \n";} }template <class CharT, class T> void SuffixTree<CharT,T>::print(std::basic_ostream<CharT> * stream) {assert(!"function not supported"); }template <> void SuffixTree<char,void*>::print(std::ostream * stream) // || // std::basic_ostream<char> {*stream<<"Root ";__printSuffixTreeNode<char,void*>(root_, 0,2, stream); }template <class T> static void __dotPrintNode(Node<char,T>const*node,std::ostream*stream) {char buf[16];int i,I,j;for(i=0,I=node->branches.size(); i<I; ++i) {Node<char,T> const* p = node->branches[i];*stream<<"node"<<x08(buf,p)<<" [label=\"\"";if(p->branches.empty())*stream<<" peripheries=2 width=0.1 height=0.1";*stream<<"];\nnode"<<x08(buf,node);*stream<<" -> node"<<x08(buf,p)<<" [label=\"";for(j=0;j<p->len;++j)if(p->str[j])*stream<<p->str[j];else*stream<<"$";*stream<<"\"];\n";__dotPrintNode(p,stream);}if(!node->branches.empty()) {*stream<<"node"<<x08(buf,node);*stream<<" -> node"<<x08(buf, (node->link))<<" "<<"[color=red];\n";} }template <class CharT, class T> void SuffixTree<CharT,T>::dot(std::basic_ostream<CharT> *) {assert(!"function not supported"); }template <> void SuffixTree<char,void*>::dot(std::ostream * stream) {char buf[16];*stream<<"digraph stree {\n";*stream<<"node"<<x08(buf, root_)<<" [label=\"root\"];\n";*stream<<"node"<<buf<<" -> node"<<buf<<";\n";*stream<<"node [width=0.2 height=0.2] \n";__dotPrintNode<void*>(root_, stream);*stream<<"}\n"; }#endif /* SUFFIXTREE_H */// vi:ts=4:foldmethod=marker7???References
| [1] | : Esko Ukkonen, Suffix tree and suffix array techniques for pattern analysis in strings, Erice School 30 Oct 2005 |
| [2] | (1,?2,?3,?4)?: Gusfield, Dan (1999) [1997]. Algorithms on Strings, Trees and Sequences: Computer Science and Computational Biology. USA: Cambridge University Press. ISBN 0-521-58519-8 |
| [3] | (1,?2)?: E. Ukkonen, On-Line Construction of Suffix Trees, Algorithmica, 14 (1995), 249-260 |
轉載于:https://www.cnblogs.com/sandy2013/p/3260009.html
總結
以上是生活随笔為你收集整理的suffix tree的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中中括号中的负数
- 下一篇: hdu 4679 树状dp