[算法]后缀树suffix tree
一、后缀树其实是把一个单词所有的后缀都加上的一棵经过合并简化的字典树(trie tree)。
如"mississip",其实就是在字典树中插入了"mississip","ississip","ssissip",sissip","issip","ssip","sip","ip","p"。这棵字典树已经可以完成后缀树的功能,只是它的空间复杂度极高。
二、后缀树就是把上面字典树中不同的分支合并成字符串
三、如何在O(N)时间内建立suffix tree
考虑字典树的建立方法,假设要插入的字符长度为n,如果每次插入后缀树都要遍历所有的n个后缀,复杂度是O(N^3)。
如需要插入“mississip”,则
m
mi,i
mis,is,s
miss,iss,ss(本来还有一个s,但s已经包含在ss中了)
…………如此类推,每次迭代插入单词的一个字符,如此字符的分支不存在则新开辟一个。
当要插入第五个i时,missi,issi,ssi,i。问题就来了,因为上面忽略了一个s,所以其实这里就少了si这个分支。
如果需要解决这个漏分支的问题,直观的方法是每次插入一个字符都遍历它前面的所有后续,但这个效率太低。
解决方法是,如当前的后缀树有k个分支(叶子),则只需考虑最后n-k长的后缀即可。原因是按照上面的算法每次插入一个字符,如已有3个分支,则单词的前3个后缀肯定存在,后面的就不确定了。如此时miss,iss,ss这三个分支肯定存在。例如现有3个分支,需要插入"missi"长为5的单词,现在需要插入i,则只需检查5-3=2最后2个的后续,就是si和i。就是
(1)在已有的3个分支中直接加上新的i,missi,issi,ssi
(2)检查最后2个的后续,si不存在,i不存在,因此再加上这两个分支
使用此方法,建立后缀树的复杂度为O(n)。
四、使用suffix link指针来加速
在每一个节点中添加一个指针,指向下一个应添加字符的节点处。用一个指针p来记录当前最长后缀(单词本身)的最后一个节点E。添加下一个字符时,由p跳转到E直接添加,然后通过E的suffix link跳到下一个添加处判断是否需要添加,直到跳转到root结束。
插入m时,把m节点的suffix link指向root。并保存m指针。
利用保存的指针直接 挑战到m,新建i,保存i的指针。利用m的suffix link跳转至root,root下没i于是新建i,并将i的suffix link连接到root。 以后如此类推。