【问题标题】:Longest common substring in two sequences of strings两个字符串序列中的最长公共子串
【发布时间】:2013-10-03 11:27:43
【问题描述】:

刚刚学习了最长公共子串算法,我对这个问题的一个特定变体很好奇。描述如下-:

给定两个非空字符串序列,X = (x1, x2, x3,....,x(n)) 和 Y = (y1, y2, y3,..., y(m)) ,其中 x(i) 和 y(i) 是字符串,在 X 中找到 最长 字符串,它是 Y 的 all 字符串的子字符串。

我有一个函数substring(x, y),它返回描述 x 是否是 y 中的子字符串的布尔值。显然,我必须将 Y 中的所有字符串连接起来形成一个大字符串,例如,用 B 表示。我想到了以下方法-:

  • 朴素:首先将 X 中的所有字符串连接起来形成一个字符串 A(n)。应用 substring(A(n), B) - 这包括在字符串 A(n) 中向后迭代。如果为真,则算法在此处结束并返回 A(n) - 或包含在所述子字符串中的任何部分。如果不是,则继续申请 (A(n - 1), B) 等等。如果 X 中不存在这样的字符串,则返回空字符串。

显然,这种方法会占用相当多的运行时间,具体取决于实现。假设我使用迭代方法,在每次迭代中,我必须在该级别/索引处向后迭代字符串,然后应用 substring()。这将需要至少两个循环和O(size(B) * maxlength(x1, x2,...)) 最坏情况时间,或者更多取决于 substring() (如果错误,请纠正我)。

我想到了基于后缀树/数组的第二种方法。

  • 广义后缀树:我在O(maxlength(y1, y2,...)(?) 中使用 Ukkonen 算法构建了序列 Y 的 GST。我对后缀树咬伤的知识缺乏。我相信后缀树方法会大大减少查找子字符串的运行时间(以空间为代价),但我不知道如何实现该操作。

如果有更好的方法,我很想知道。

编辑:抱歉,如果我似乎放弃了这个话题。

如果我不使用 GST,而是使用一些标准数据结构,例如堆栈、队列、集合、堆、优先级队列等,会怎样?必须对序列 X 进行排序,首先是最大的字符串。如果我将它存储在一个字符串数组中,我将不得不使用诸如mergesort/quicksort之类的排序算法。目标是尽可能获得最有效的运行时间。

我不能将 X 存储在一个结构中,当它自己构建时,它会自动对其元素进行排序?那么最大堆呢?

后缀树似乎是以这种方式查找子字符串的最佳方式。还有其他我可以使用的数据结构吗?

【问题讨论】:

  • 你想要一个字符串,它是 Y 中所有字符串的子字符串。但 B 是所有字符串的并集(不是交集)。因此,您将找到作为 Y 中至少 1 个字符串的子字符串的字符串。我错过了什么吗?
  • m 个字符串的子串不一定是这 m 个字符串之一的子串。因此以下陈述是错误的:“显然,我必须连接 Y 中的所有字符串以形成一个大字符串,例如,用 B 表示。”
  • Y 是一个字符串序列。那我不应该寻找所有字符串的联合/连接吗?这就是我在阅读“Y 中的所有字符串”时所想的。我可能把这搞砸了。可以举个例子解释一下吗?

标签: string algorithm data-structures


【解决方案1】:

首先,将最长字符串的数组 X 排序为更短。这样,X 中作为所有 Y 字符串的子字符串的第一个字符串就是解。

多处理器算法将是解决用所有 Y 字符串快速测试每个 X 字符串问题的最佳方法。

【讨论】:

  • 我喜欢多处理器方法——肯定会提供一些成果。不过,我正在寻找一种更加面向数据结构的方法。
  • 我不知道你想使用什么语言,但是在 c 中你有库 mpich2 而在 R 中有包 foreach。
【解决方案2】:

这是我对解决您问题的想法;我不确定所有内容,因此如果您认为值得努力,欢迎 cmets 对其进行改进。

从计算 Y 中所有字符串的所有公共子串开始。首先取两个字符串,并构建所有公共子串的树。然后,对于 Y 中的每个其他字符串,从映射中删除该字符串中未出现的每个子字符串。复杂度与 Y 中的字符串数量成线性关系,但我无法确定树中有多少元素,因此无法估算出最终的复杂度。

然后找到 X 中最长的字符串,它是树中一个的子字符串。

必须进行一些改进以使树尽可能小,例如只保留不是其他子串的子串。

【讨论】:

  • 这种方法会不会需要很长时间,因为我们必须一次处理两个字符串?
  • @TheRedBlackTree 您一次只取前两个字符串;然后你一次只取一个字符串,并从树中删除字符串中不存在的所有子字符串。我认为整个事情可以通过专用数据结构来加速,我只是按照我的想法给出。
【解决方案3】:

写作 |Y|对于集合 Y 中的字符串数,以及它们的总长度 len(Y):

  1. 将 Y 中的字符串处理为 generalized suffix tree(例如,使用 Ukkonen's algorithm)。假设字母大小不变,耗时 O(len(Y))。

  2. 根据该节点标识的字符串是否属于Y中的所有字符串来标记后缀树中的每个节点。耗时O(|Y| len(Y))。

  3. 对于X中的每一个字符串,在后缀树中查找,看看该节点是否被标记为属于Y中的所有字符串。输出最长的这样标记的字符串。耗时 O(len(X))。

总时间:O(|Y| len(Y)) + O(len(X))。

【讨论】:

  • 这似乎是一种非常可行的方法。我想知道是否可以降低复杂性,但如果我要使用后缀树,我想这是处理它的最佳方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-30
  • 1970-01-01
  • 1970-01-01
  • 2021-12-30
  • 2013-09-27
相关资源
最近更新 更多