最长的重复子串

时间:2010-12-17 18:27:56

标签: string algorithm sorting

这是关于Jon Bentley的“Programming Pearls”中描述的最长的重复子串算法的问题。我记得,他们为输入字符串构建一个后缀数组,对后缀进行排序,并扫描它们。

看起来最昂贵的一步就是整理。如果我们使用比较排序,那么比较的数量是O(N * logN),其中N是输入字符串的大小。由于字符串比较为O(字符串长度),因此排序为O(N ^ 2)。

有意义吗?

因此,该算法在空间中为O(N ^ 2)和O(N)。可以做得更好吗?

2 个答案:

答案 0 :(得分:3)

虽然你可以从后缀树构造一个后缀数组,但没有什么意义。它将消除后缀数组的主要优点(除了一般简单性):微小的内存消耗。

有一种简单的方法可以在O(n*logn)时间内对后缀数组进行排序。 (因此,它经常用于各种算法竞赛,作为复杂后缀尝试的替代方案)

基本思路如下。我们会分阶段对后缀进行排序,在阶段ii = 0, 1, 2, 3, ...)中,只考虑每个后缀的前2^i个字符。

按照第一个字符对后缀进行排序是微不足道的,对您来说应该没问题。在这个(“第0个”)阶段之后,我们将部分排序后缀数组和另一个数组,其中包含后缀分区到桶中:每个桶保留带有相同第一个符号的后缀。

现在,想象一下我们已经完成了阶段i并立即处理阶段i + 1。我们需要比较属于同一个存储桶的两个后缀s(t)s(q)。 (让s(t)为原始字符串中位置t的后缀。)
由于第一个2^i字符对于它们是相同的,我们只需考虑下一个2^i字符(因此,总数将为2^(i+1))。但是没有第一个s(t)符号的后缀2^is(t + 2^i)。因此,我们只需要根据他们的第一个s(t + 2^i)符号对s(q + 2^i)2^i进行比较,我们已经从阶段i获得了此信息。

结束。第一次实施它可能有点棘手(也是一个很好的练习),但这是个主意。请注意,我们从原始字符串中读取实际字符的唯一时间是步骤0。然后,在步骤i上,我们仅使用步骤i - 1中的结果。

修改
This original paper from 1989提供了更多细节。 (更多细节不是必要的,理解它和实施比需要更复杂,恕我直言。)

答案 1 :(得分:1)

朴素排序会使后缀数组构造为O(n ^ 2logn),而不是O(n ^ 2)。

但是,有一些方法可以在O(n)中构造后缀数组[一种简单的方法是构造一个后缀树并将其转换为后缀数组 - 后缀树结构为O(n)]。

我不知道是否可以用少于O(n)的空间。