假设有两个序列S1[1...m]和S2[1...n],需要寻找它们之间的一个最长公共子序列。
例如,假定我们有如下两个序列:
S1:I N T H E B E G I N N I N G
S2:A L L T H I N G S A R E L O S T
则S1和S2的一个最长公共子序列为THING。又如:
S1:A B C B D A B
S2:B D C A B A
则S1和S2的一个最长公共子序列为BCBA。
这里需要注音的是,一个子序列不一定必须是连续的,中间可以被其他字符分开。最长公共子序列不一定只有一个,而我们要寻找的是其中一个。
设c[i, j] = LCS(S1[1...i], S2[1...j]) ,表示S1[1...i]和S2[1...j]的最长子序列。那么问题的解为c[m, n]。
仔细观察我们可以知道:
如果S1[i] = S2[j],那么c[i, j] = c[i-1, j-1] + 1,
如果S1[i] ≠ S2[j],那么c[i, j] = max{c[i-1, j], c[i, j-1}(证明略)。那么我们就将原问题分解为相似的子问题。而子问题很多是重复的,所以我们将子问题的结果存放到表中,防止重复计算。
最长公共子序列的Python代码如下:
#!/usr/bin/python
# -*- coding: utf8 -*-
S1 = ['I', 'T', 'E', 'I', 'N', 'G']
S2 = ['T', 'I', 'N', 'G', 'E']
#后面几组供测试使用
#S1 = ['A', 'N', 'T', 'H']
#S2 = ['A', 'L', 'N', 'T']
#S1 = ['I', 'N', 'T', 'H', 'E', 'B', 'E', 'G']
#S2 = ['A', 'L', 'L', 'T', 'H', 'I', 'N', 'G']
#S1 = ['A', 'B', 'C', 'B', 'D', 'A', 'B']
#S2 = ['B', 'D', 'C', 'A', 'B', 'A']
#存储结果的数组,c[i+1, j+1] = LCS(S1[0...i], S2[0...j]),0<=i= c[i + 1][j]:
c[i + 1][j + 1] = c[i][j + 1]
b[i][j] = "↑"
else:
c[i + 1][j + 1] = c[i + 1][j]
b[i][j] = "←"
return c[len(S1)][len(S2)]
#根据表b构造出一个LCS
def print_LCS(b, X, i, j):
if i == -1 or j == -1:
return
if b[i][j] == "↖" :
print_LCS(b, X, i-1, j-1)
print X[i]
elif b[i][j] == "↑":
print_LCS(b, X, i-1, j)
else:
print_LCS(b, X, i, j-1)
print(LCS(S1, S2))
print_LCS(b, S1, len(S1) - 1, len(S2) - 1)