我一直在使用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的非重叠子串中,如果不是则删除它们。问题是这不能提供最佳解决方案。
答案 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{}
选项的辅助矩阵重建对齐,并在矩阵完成时从最后一个到第一个。
示例:强>
bcbac
和cbcba
。 k=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
现在,重建对齐 - 从最后一个(右下角)单元格开始:
重建时的访问顺序为:(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