给定字符串s
,找到时间复杂度O(|s|)
中最长的双后缀。
示例:对于字符串banana
,LDS为na
。对于abaabaa
,它是baa
。
显然我想过使用后缀树,但是我很难找到双后缀。
答案 0 :(得分:0)
反转字符串并构建稀疏数组P[i][j]
,其中i
从0
到log(n)
,j
从0
到{ {1}},n-1
是字符串的长度。 n
是指从位置P[i][j]
和长度j
开始的后缀的等级。因此,如果2^i
,则索引P[i][j]=P[i][k]
和2^i
的后缀的第一个j
字符相等。
现在,您的问题减少为找到k
的最长公共前缀(反向字符串的开头)和索引0
的另一个后缀,例如i
。
只需在LCP >= i
时间内使用P
数组,通过比较这两个后缀的前log(n)
个字符并逐渐减少2^x
来计算LCP。
总复杂度为x
。
以下是可用的C ++源代码:https://ideone.com/aJCAYG
答案 1 :(得分:0)
我认为Gene的解决方案更容易实现,因为它不依赖于树状结构,而是依赖于阵列,它也可能更加硬件友好。
但是既然你提到了后缀树,让我们看一下基于后缀树的解决方案!我将假设您使用结束标记来标记您在树中插入的字符串的结尾。为了说明这一点,下面是为abaabaa
示例构建的后缀树的表示:
$ - ##
b a a - $ - ## // Longest double suffix: P is the first dash, N the second
b a a $ - ## // N' is the dash
a - $ - ##
a - $ - ##
b a a $ - ##
b a a - $ - ##
b a a $ - ##
当 N 是后缀树中的节点时,我们将表示 | N | 由 N 表示的子字符串的长度。< / p>
如何在后缀树中表征“双后缀”?那么它是一个终端节点 N ,其父节点具有特定属性:让 P 成为双后缀的父节点,然后:
$
)。baa$
)。如果我们从 P 向下走树,使用后缀,我们最终会进入另一个后缀节点 N'(沿着树向下走实际需要)baa
)。鉴于此,您只需迭代后缀节点并测试此条件。如果以长度递减的顺序迭代后缀,则可能会贪婪:第一个匹配必然是最长的双后缀。
请注意,我们可以在检查长度| S | / 2的后缀并且只迭代奇数长度的后缀之后停止搜索(不要忘记我们在字符串中添加结束标记)
构建后缀树是O(|S|)
设 N'为后缀节点,N
为后缀长度为(| N'| -1)/ 2 + 1 的后缀节点。假设树的正确构造:
O(1)
O(1)
,特别是,这适用于 P N 和的父节点N' $
)是O(1)
O(1)
由于我们按照长度的递减顺序迭代后缀,我们必须关注N'
后缀(加倍后缀,即示例中的baabaa$
),所以我们只需要:< / p>
|N'| = 2.|N| - 1
:O(1)
N
的父级:O(1)
$
:O(1)
证明: (我们忽略以下证明中的结束标记)
如果导致真正的评估,上面的3个步骤证明存在长度 2的后缀。| P | 以 P 表示的子字符串开头,这也是一个后缀。由于此子字符串是后缀,因此长度 2。| P | 的后缀必须以它结尾,因此由两次出现的子字符串QED组成。
由于我们最多会为(|S|/2 + 1)/2
个后缀执行此步骤,因此在最坏的情况下,识别步骤为O(|S|)
。
因此总体复杂性为O(|S|)
。