关于算法:真的很难理解后缀树

really hard to understand suffix tree

我一直在寻找有关后缀树的教程。在So中,我发现了2篇关于理解后缀树的文章:1,2。

但我不能说我理解如何建造一个,哎呀。在斯基纳的《算法设计手册》中,他说:

Since linear time suffix tree construction algorithms are nontrivial,
I recommend using an existing implementation.

那么,后缀树的在线构造算法这么难吗?有人能把我带到正确的方向去理解它吗?

不管怎样,切到追逐,除了建设,还有一件事,我不明白后缀树。因为后缀树中的边只是一对整数(对吗?)指定子字符串的起始和结束位置,然后如果我想在这个后缀树中搜索一个字符串x,我该怎么做?取消后缀树中那些整数的引用,然后将它们逐个与x进行比较?不能这样。


首先,有很多方法可以构造后缀树。维纳(1973)提出的原始O(N)方法,麦克赖特(1976)提出的改进方法,Ukkonen(1991/1992)最著名的改进方法,以及一些进一步的改进,主要与实施和存储效率考虑有关。其中最值得注意的可能是Giegerich和Kurtz高效地实现了懒惰的后缀树。

此外,由于后缀数组的直接构造在2003年的O(N)时间内成为可能(例如,使用倾斜算法,但也有其他方法),并且由于对

  • 使用后缀数组模拟后缀树(例如Abouelhoda/Kurtz 2004)
  • 压缩后缀数组(参见navarro/m?Kinen 2007的一项调查)

后缀数组通常优先于后缀树。因此,如果您打算为特定目的构建高度优化的实现,那么您可能需要研究后缀数组构造算法。

但是,如果你对后缀树的构造感兴趣,特别是Ukkonen算法,我建议你仔细看看这篇文章中的描述,你已经提到了,我们试图一起改进这个描述。这绝对不是一个完全直观的解释。

要回答有关如何将输入字符串与边缘标签进行比较的问题:由于构造和查找过程中的效率原因,每个边缘标签的初始字符通常存储在节点中。但其余的必须在主文本字符串中查找,正如您所说的,这确实会导致问题,特别是当字符串太大以至于无法在内存中保存时。这(加上后缀树和任何直接实现的树一样,是一个包含大量指针的数据结构,这些指针消耗大量内存,使得维护引用位置和从内存缓存中受益变得困难)是后缀树比反向索引更难处理的主要原因之一。


如果您将后缀数组与一个LCP表和一个子表组合在一起(当然您应该这样做),那么您实际上会得到一个后缀树。本文提出了金、朴、金对后缀树进行线性化的观点。LCP表实现了一个相当笨拙的自下而上的遍历,而子表实现了任何一种简单的遍历。因此,在我看来,使用指针导致引用位置问题的后缀树的故事是过时的信息。因此,只要使用底层后缀数组实现树,后缀树就是"正确且简单的方法"。

Kim、Park和Kim的论文描述了Abouelhoda等人题为"用增强后缀数组替换后缀树"的论文中的一种方法变体。Kim等人认为这是后缀树的实现,而不是替换。此外,Abouelhoda等人的构造细节在Kim等人中更为简单直观。


这里有一个Ukkonen对后缀树(加上后缀数组,lcp数组)的线性构造的实现:http://code.google.com/p/text-indexing/。与suffixtree.js一起提供的可视化可能有帮助