通过后缀数组的最长公共子字符串:我们真的需要唯一的标记吗?

时间:2019-08-29 13:54:53

标签: suffix-array longest-substring

我正在阅读有关LCP数组及其与后缀数组结合使用的信息,以解决“最长的公共子字符串”问题。 This video指出,用于分隔单个字符串的标记必须是唯一的,并且不能包含在任何字符串本身中。

除非我弄错了,否则的原因是这样,当我们构造LCP数组时(通过比较相邻后缀共有多少个字符),在两个前哨点恰好位于相同位置的情况下,我们不计算前哨值我们正在比较的两个后缀中的索引相同。

这意味着我们可以编写如下代码:

for each character c in the shortest suffix
    if suffix_1[c] == suffix_2[c]
        increment count of common characters

但是,为了实现这一点,我们需要跳过一些箍以确保我们使用独特的哨兵which I asked about here.

但是,一种更简单(实现)的解决方案不是简单地计算共同字符的数量,在达到(单个,唯一)定点字符时停止,如下所示:

set sentinel = '#'
for each character c in the shortest suffix
    if suffix_1[c] == suffix_2[c]
        if suffix_1[c] != sentinel
            increment count of common characters
        else
            return

或者,我在这里缺少基本的东西吗?

2 个答案:

答案 0 :(得分:0)

实际上我只是设计了一种根本不使用哨兵的算法:https://github.com/BurntSushi/suffix/issues/14

连接字符串时,还请记录边界索引(例如,对于长度为4、2、5的3个字符串,将记录边界4611,因此我们知道concatenated_string[5]属于第二个原始字符串,因为4<= 5 < 6)。

然后,要进行二进制搜索,以识别每个后缀属于哪个原始字符串。

答案 1 :(得分:0)

简短的版本是“这主要是关于后缀数组构建算法如何工作的人工产物,与LCP计算无关,因此,只要您的后缀数组构建算法不需要这些标记,就可以安全地跳过它们。”

更长的答案:

从总体上讲,视频中描述的基本算法如下:

  1. 为字符串T 1 和T 2 构造广义后缀数组。
  2. 为生成的后缀数组构造一个LCP数组。
  3. 遍历LCP数组,查找来自不同字符串的相邻后缀对。
  4. 在任何两个这样的字符串之间找到最大的LCP;叫做k。
  5. 从两个后缀之一中提取前k个字符。

那么,哨兵在这里出现在哪里?它们主要按步骤(1)和(2)提出。该视频暗示使用线性时间后缀数组构造算法(SACA)。用于生成两个或多个字符串的后缀数组的大多数快速SACA都假设,作为其操作的一部分,在这些字符串的末尾有不同的标记,并且算法的内部正确性通常依赖于此。因此,从这个意义上讲,可能纯粹是为了使用快速SACA而添加了标记标记,完全独立于以后的使用。

(为什么SACA需要这个?一些最快的SACA,例如SA-IS算法,假设字符串的最后一个字符是唯一的,在字典上位于所有字符之前,并且在其他任何地方都没有出现。为了使用该算法包含多个字符串,您需要某种内部定界符来标记一个字符串结束并在另一个字符串开始的位置。该字符需要充当强力的“并且我们现在已经处理了第一个字符串”字符,这就是为什么它需要在字典上排在所有其他字符之前。)

假设您以这种方式将SACA用作黑匣子,那么从现在开始,这些哨兵就完全没有必要了。它们不能用来区分哪个后缀来自哪个字符串(这应该由SACA提供),并且它们不能成为相邻字符串之间重叠部分的一部分。

因此,从某种意义上讲,您可以将这些标记看作是使用快速SACA所需的实现细节,为了获得快速的运行时间,您需要这样做。