具有固定长度子串的最长公共子序列

时间:2014-03-17 09:49:36

标签: algorithm dynamic-programming

我一直在使用Longest Common Subsequence(LCS)来查找序列之间的相似内容。以下动态编程代码计算答案。

def lcs(a, b):
    lengths = [[0 for j in range(len(b)+1)] for i in range(len(a)+1)]
    # row 0 and column 0 are initialized to 0 already
    for i, x in enumerate(a):
        for j, y in enumerate(b):
            if x == y:
                lengths[i+1][j+1] = lengths[i][j] + 1
            else:
                lengths[i+1][j+1] = \
                    max(lengths[i+1][j], lengths[i][j+1])
    # read the substring out from the matrix
    result = ""
    x, y = len(a), len(b)
    while x != 0 and y != 0:
        if lengths[x][y] == lengths[x-1][y]:
            x -= 1
        elif lengths[x][y] == lengths[x][y-1]:
            y -= 1
        else:
            assert a[x-1] == b[y-1]
            result = a[x-1] + result
            x -= 1
            y -= 1
    return result

然而,我已经意识到我真正想要解决的是有点不同。给定一个固定的k我需要确保公共子序列只涉及长度恰好为k的子串。例如,设置k = 2并让两个字符串为

A = "abcbabab"
B = "baababcc"

我需要的子序列是“ba"+"ab" = baab

是否可以修改动态编程解决方案来解决这个问题?

最长的常见子序列问题只是k = 1的情况。

无效的方法。

如果我们执行上面的LCS算法,我们可以从动态编程表中获取对齐,并检查这些符号是否出现在两个输入序列中长度为k的非重叠子串中,如果不是则删除它们。问题是这不能提供最佳解决方案。

1 个答案:

答案 0 :(得分:2)

当相关索引中的两个字符串具有匹配的子字符串(而不是现在的匹配字母)时,校正基本上是正确的。

这个想法不是简单地在原始解决方案中检查大小为1的子字符串,而是检查长度为k的子字符串,并在解决方案中添加1(并且跳过' by字符串中的k

应转换为DP解决方案的递归方法的公式为:

f(i,0) = 0
f(0,j) = 0
f(i,j) = max{
          f(i-k,j-k) + aux(s1,s2,i,j,k)
          f(i-1,j)
          f(i,j-1)
             }

其中aux(s1,s2,i,j,k)是一个旨在检查两个子串是否匹配的函数,并定义为:

aux(s1,s2,i,j,k) = 1         | if s1.substring(i-k,i) equals s2.substring(j-k, j)
                   -infinity | otherwise

您可以稍后使用标记max{}选项的辅助矩阵重建对齐,并在矩阵完成时从最后一个到第一个。

示例:

bcbaccbcbak=2

f生成的矩阵:

      c   b   c  b   a
   0  0   0   0  0   0
b  0  0   0   0  0   0
c  0  0   0   1  1   1   
b  0  0   1   1  1   1
a  0  0   1   1  1   2
c  0  0   1   1  1   2

为了重现对齐,您可以选择'矩阵:

1 - 选择f(i-k,j-k)+ aux(s1,s2,i,j,k)
2 - 选择f(i-1,j)
3 - 选择f(i,j-1)
d - 不在乎,(所有选择都很好)
x / y-表示x或y中的一个。

c      b      c     b      a

b   2/3    2/3    2/3   2/3    2/3
c   2/3    2/3     1     2      2   
b   2/3     3     2/3    d     2/3
a   2/3     3     2/3   2/3     1
c   2/3     3     2/3   2/3     3

现在,重建对齐 - 从最后一个(右下角)单元格开始:

  1. 它' 3' - 向上移动,不要在路线上添加任何东西。
  2. 它是1 - 我们需要添加' ba'对齐。 (align =' ba'目前)。向上移动k并离开。
  3. 它是1,广告''对齐。当前对齐:' bcba'。向上移动k并离开。
  4. 它的2/3 - 向左或向上移动。
  5. 重建时的访问顺序为:(0表示 - 未访问,许多单元格中的数字表示其中任何一个都可以)。

          c   b   c  b   a
       0  4   0   0  0   0
    b  4  3   0   0  0   0
    c  0  0   0   0  0   0   
    b  0  0   0   2  0   0
    a  0  0   0   0  0   0
    c  0  0   0   0  0   1