依次检查长度 >= N 的重复子序列

Posted

技术标签:

【中文标题】依次检查长度 >= N 的重复子序列【英文标题】:Check for duplicate subsequences of length >= N in sequence 【发布时间】:2014-07-03 13:09:12 【问题描述】:

我有一个值序列,我想知道它是否包含某个最小长度的重复子序列。例如:

1, 2, 3, 4, 5, 100, 99, 101, 3, 4, 5, 100, 44, 99, 101

包含两次子序列3, 4, 5, 100。它还包含两次子序列99, 101,但该子序列是两个短的需要关心。

有没有一种有效的算法来检查这样的子序列是否存在?我对序列的定位不是特别感兴趣(尽管这对验证很有帮助),我主要只对真/假答案感兴趣,给定一个序列和一个最小子序列长度。

到目前为止,我唯一的方法是暴力搜索:对于序列中的每个项目,找到该项目出现的所有其他位置(已经在 O(N^2)),然后向前走一步从每个位置的时间,看看下一个项目是否匹配,并继续前进,直到我发现不匹配或找到足够长度的匹配子序列。

我有但未能发展成实际方法的另一个想法是构建所有序列的树,以便每个数字都是一个节点,并且是它之前的数字的子节点,无论那个节点恰好已经在树中。

【问题讨论】:

看起来您正在寻找子字符串,而不是子序列。查看什么是子序列:en.wikipedia.org/wiki/Subsequence,“子序列是可以通过删除一些元素而不改变其余元素的顺序从另一个序列派生的序列”。对于子序列和子字符串都存在有效的算法。 后缀树为 O(n) 【参考方案1】:

N 的任何值都有O(k) 解决方案(k - 整个序列的长度)。

解决方案 #1: 为输入序列构建一个后缀树(使用 Ukkonen 算法)。 遍历具有两个或更多子节点的节点,并检查其中至少一个节点是否具有深度 >= N

解决方案#2:为输入序列构建一个后缀自动机。遍历所有正确上下文包含至少两个不同字符串的状态,并检查其中至少一个节点是否有距离>= N从自动机的初始状态。

解决方案#3:也可以使用后缀数组和最长公共前缀技术(为输入序列构建后缀数组,计算最长公共前缀数组,检查是否存在一对具有公共前缀的相邻后缀长度至少为N)。

这些解决方案的时间复杂度为O(k),假设字母大小是恒定的(字母由输入序列的所有元素组成)。 如果不是这种情况,仍然可以获得O(k log k)最坏情况时间复杂度(通过将所有转换存储在树中或map中的自动机中)或O(k)平均使用hashmap

P.S 我在这里交替使用术语 stringsequence

【讨论】:

【参考方案2】:

如果您只关心长度正好为 N 的子序列(例如,如果只想检查是否有重复项),那么有一个二次解:对每个子序列使用 KMP algorithm。

假设整个序列有k个元素。

对于每个长度为 N(O(k) 个)的子序列:

构建其失效函数(耗时 O(N)) 在序列的剩余部分中搜索它(花费 O(k))

所以,假设N

【讨论】:

【参考方案3】:

由于您的列表是无序的,因此您必须至少访问每个项目一次。

我的想法是,您首先要浏览您的列表并创建一个字典,在其中存储数字作为键以及它出现在序列中的所有索引。喜欢:

Key: Indices
  1: 0 
  2: 1 
  3: 2, 8
  ....

其中数字 1 出现在索引 0 处,数字 2 出现在索引 1 处,数字 3 出现在索引 2 和 8 处,依此类推。

创建后,您可以查看字典键并开始将其与其他位置的序列进行比较。这应该可以节省一些蛮力,因为您不必每次都通过初始序列重新访问每个数字。

【讨论】:

以上是关于依次检查长度 >= N 的重复子序列的主要内容,如果未能解决你的问题,请参考以下文章

最长连续不重复子序列

单增最长子序列

最长连续不重复子序列

算法总结之 最长递增子序列

codeforces 1077D

最长连续不重复子序列