什么样的算法更适合无序序列匹配问题?

时间:2010-10-08 10:37:59

标签: algorithm sequence matching

如果我有两个序列(例如,字符串)

//   01234567890123456789012  
a = "AAACDDFFFEE1122VV1VAADD"

//   0123456789012345678901
b = "DDFFAA11221DHHVV1VAAFE"

我想知道从b到a的最佳子串匹配(无序),例如:

optimal (6 matched parts, 19 characters of a matched)
b         a
DDFF   -> DDFF     (4,7)
AA     -> AA       (0,1)
1122   -> 1122     (11,14)
1     
D      -> D        (21)
HH
VV1VAA -> VV1VAA   (15,20)
FE     -> FE       (8,9)

还有另一种解决方案,但不是最佳解决方案:

not optimal (8 parts matched, 19 characters of a matched)
b        a
DDFF  -> DDFF  (4,7)
AA    -> AA    (0,1)
1122  -> 1122  (11,14)
1     -> 1     (17)
D     -> D     (21)
HH
VV    -> VV    (15,16)
1     
VAA   -> VAA   (18,20)
FE    -> FE    (8,9)

什么样的算法更适合这个问题? (我需要最佳结果,性能至关重要)。

感谢。

2 个答案:

答案 0 :(得分:1)

有趣的问题,您可以使用Boyer-Moore(http://en.wikipedia.org/wiki/Boyer - Moore_string_search_algorithm)或KMP(http://en.wikipedia.org/wiki/Knuth - Morris在O(| a |。| b | + | b | ^ 2)中解决它-Pratt_algorithm)算法或任何其他线性时间字符串搜索算法。

  • 对于每个b [0..i] ty,在字符串a(在O(| a | + i)中)找到它,直到找不到它为止
  • 你知道你可以找到b [0..i]而不是b [0..i + 1],所以你有b [0..i]的匹配,你继续b [i + 1。 .I + 1],b [I + 1..i + 2] ..
  • 最后,b的每个部分是否匹配,如果匹配的话,是否尽可能大。

总复杂度最多为总和(O(| a | + i),i = 0 .. | b |)= O(| a |。| b | + | b | ^ 2)但它可以很多如果只能在a中找到b的小子串,那就更小了。

编辑:

上述方法很贪婪,不会减少部件数量,但我认为它会最大化匹配的字符总数。

<小时/> 关于最佳解决方案的想法:

  • 表示| b |的每个| b | ^ 2子串确定是否可以在| a |中找到它,并且只保留它的情况
  • 我们需要在这些字符串中找到它们的子集:
    • 其中任何两个之间没有重叠
    • 长度之和最大
    • 等长,字符串数必须最小

总和很容易计算,因为一个非常简单的解决方案是只匹配大小为1的子串:然后长度是a和b之间的公共字母数。

因此,如果我们将b的大小为1的子字符串(即使是不在a中的字母)添加到上面的匹配字符串集中,我们需要找到b的最小集合覆盖,而不会重叠。

一般的封面是NP完全的,但是这里有没有重叠的约束,它有帮助吗?

我正在研究它。

确实,NP-complete:http://www.springerlink.com/content/n73561q050w54pn6/

您可能想要寻找近似算法....

答案 1 :(得分:0)

如果我理解你的问题,你想要找到一组两个给定字符串的非重叠公共子串,这些子串最大化公共子串的总长度,并且最小化公共子串的数量。我将提出以下启发式:找到两个字符串中最长的公共子字符串(LCS),删除它,重复。我不能证明这是最优的,但我有一个非常有效的算法

所以在你的例子中 AAACDDFFFEE1122VV1VAADD DDFFAA11221DHHVV1VAAFE LCS = VV1VAA

AAACDDFFFEE1122DD DDFFAA11221DHHFE

LCS = DDFF

AAACFEE1122DD AA11221DHHFE

LCS = 1122

AAACFEEDD AADHHFE

等等

算法如下 1)使用基于后缀树的标准LCS算法 1.1构建连接的两个字符串的后缀树和唯一的终止符 1.2用1,2或两者标记每个节点,这取决于那里生根的子树是否有来自其中一个或两个字符串的叶子 1.3计算每个节点的字符串深度 1.4找到标记为1和2的字符串最深的节点 2)删除以该节点为根的子树,并更新其上方节点的标签 3)从1.4重复

当树没有标记为1和2的节点时,算法终止 1.1可以在与两个弦的长度之和成比例的时间内完成 1.2,1.3和1.4只是树遍历 2如果树实现正确并且更新受LCS长度的限制,则删除应该是恒定时间 3再次是树遍历但是树较小

所以这是一个优化,为了避免重复的树遍历,让我们添加步骤1.35:按字符串深度排序两个标签的内部节点(在单独的数据结构中,树仍在那里)。现在,您可以扫描排序的节点列表,执行2)并重复。通过这种优化,如果你可以使用基数排序,看起来算法是线性时间,你无法以渐近的方式击败它。

我希望这是正确和清晰的,我相信在听起来很明显之前你需要先熟悉一下后缀树文献。我推荐Dan Gusfield的书“字符串,树和序列算法”,特别是7.4节如果你有问题,请告诉我。