给定一个(修改/损坏)后缀树,它在每个边缘存储当前子字符串的开头和结尾,但不存储子字符串本身,即后缀树,如下所示:
这棵树代表字母表上的字符串“banana”:{a,b,n}。
我正在寻找的算法是找到那种树所代表的字符串,对于上面的例子,我希望算法找到“banana”。 我希望在复杂的O(| string |)中使用| string |是要搜索的字符串的长度。 可以假设:
字母表的大小是常量,每个字符串都从索引1开始。
答案 0 :(得分:0)
让我们从一些多项式时间解决方案开始:
让我们将字符串中的所有字符划分为等价类。
我们已经知道:它是一个特殊的$
符号。
归纳假设:我们假设我们已经将长度为k
的后缀的所有字符正确地划分为等价类。我们也可以正确地使用长度为k + 1
的后缀。
证明:让迭代遍历长度为i <- 1...k
的所有长度,并检查长度为k
的后缀和长度为i
的后缀的最长公共前缀的长度是否为不是零。如果相应叶子的最低共同祖先不是树的根,则它不为零。如果我们找到了这样的后缀,我们知道它的第一个字母等于当前后缀的第一个字母。因此,我们可以将长度为k + 1
的后缀的第一个字母添加到适当的等价类中。否则,它属于自己的等价类。
当所有字符被分成等价类时,我们只需要为每个类分配一个唯一的符号(如果我们需要保持正确的字典顺序,我们可以检查它们中的哪一个更早。要做到这一点。 ,我们需要查看从根开始的边的顺序。
时间复杂度为O(n ^ 3)
(有n
个就足够了,我们为其中的每一个迭代O(n)
其他足够的内容,我们在O(n)
中计算其lca (我假设我们在这里使用了一个天真的算法))。到目前为止,非常好。
现在让我们使用几个观察来获得线性解决方案:
我们真的不需要lca。我们只需要检查它不是根。因此,我们可以将所有叶子划分为基于其祖先的等价类,该祖先是根的直接子。它可以使用深度优先搜索在线性时间内完成。如果它们在同一个类中,则两个足够的最长公共前缀是非空的。
我们实际上并不需要检查所有较短的时间。我们只需要在深度优先搜索顺序中检查最靠近左侧和右侧的一个。从给定的左侧和右侧找到最接近的较小数字是一个标准问题,它有一个带有堆栈的线性解决方案。
就是这样:我们最多检查给定的两个其他两个,每个检查都是O(1)
。我们现在有一个线性解决方案。
此解决方案使用这样的字符串确实存在的假设。如果这个假设不可行,我们可以使用这个算法构造一些字符串,然后使用Ukkonnen算法构建一个线性的后缀树,并检查它与给定的完全相同。