两个字符串序列中最长的公共子字符串

时间:2013-10-03 11:27:44

标签: string algorithm data-structures

刚刚学习了最常见的子串算法,我对这个问题的特定变体感到好奇。它描述如下 - :

  

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

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

  • 天真:首先连接X中的所有字符串以形成字符串A(n)。应用子串(A(n),B) - 这包括在字符串A(n)中向后迭代。如果为true,则算法在此结束并返回A(n) - 或者其中包含的任何部分包含在所述子字符串中。如果没有,继续申请(A(n - 1),B),依此类推。如果X中不存在这样的字符串,则返回空字符串。

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

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

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

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

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

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

我是否可以将X存储在一个自动对其元素进行排序的结构中?最大堆怎么样?

看起来后缀树是以这种方式查找子串的最佳方式。我可以使用其他任何数据结构吗?

3 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

这是我对你的问题的解决方案的想法;我不确定所有内容,所以如果您认为值得付出努力,欢迎提出改进意见。

首先计算Y中所有字符串的所有常见子串。首先取两个字符串,然后构建一个包含所有公共子串的树。然后,对于Y中的每个其他字符串,从地图中删除此字符串中未出现的每个子字符串。复杂度与Y中的字符串数量呈线性关系,但我无法弄清楚树中可能有多少元素,因此我无法估计最终的复杂性。

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

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

答案 2 :(得分:1)

写作| 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))。

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