一、问题描述
给定一个序列X =
给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。最长公共子序列就是公共子序列中长度最长的子序列。
接下来简单明了地举例。X = ,X的子序列就是X序列元素按顺序组成的一个子集,比如 ,等。给定另一个序列Y = ,则就是X、Y的一个子序列,而、则是X、Y的最长公共子序列。
二、最优子结构证明
假设序列X =
①若xm = yn,那么zk=xm=yn,且Zk-1就是Xm-1和Yn-1的一个最长公共子序列。
②若xm ≠ yn,且zk ≠ xm,那么Zk就是Xm-1和Yn的一个最长公共子序列。
③若xm ≠ yn,且zk ≠ yn,那么Zk就是Xm和Yn-1的一个最长公共子序列。
证明:
①由已知,假设zk ≠ xm,那么则可以把xm(yn)放在公共子序列zk的末尾,这样就形成了一个新的公共子序列,且这个公共子序列的长度 = 原来的序列Z长度 + 1,与假设矛盾,所以 zk=xm=yn。再假设Zk-1不是Xm-1和Yn-1的最长公共子序列,说明存在一个更长的子序列,这时再把xm(yn)附在这个更长子序列的后面,则得到一个X和Y的更长的公共子序列,它的长度 > 原来的Z序列长度,与Z是X和Y的最长公共子序列矛盾,所以①成立。
②由已知,假设Zk不是Xm-1和Yn的最长公共子序列,那么存在一个比Zk更长的序列是Xm-1和Yn的公共子序列,而这个更长的序列肯定也是Xm和Yn的公共子序列,这个序列长度 > 原来Z序列,与Z是X和Y的最长公共子序列矛盾,所以②成立。
③ 同②。
三、递归方程
用c[i][j]表示Xi和Yj的最长公共子序列的长度。
0 if i = 0 or j = 0
c[i][j] = c[i-1][j-1] + 1 if i,j>0 and xi= yj
max{c[i-1][j],c[i][j-1]} if i,j>0 and xi ≠ yj
四、 Python代码实现(包括输出最长公共子序列)
import copy def DPLength(X, Y, c, b): for i in range(1, m+1): for j in range(1, n+1): if X[i-1] == Y[j-1]: c[i][j] = c[i-1][j-1] + 1 b[i][j] = 1 # 1表示该字符是在公共子序列当中的 elif c[i-1][j] >= c[i][j-1]: c[i][j] = c[i-1][j] b[i][j] = 0 # 0表示不在公共子序列中 else: c[i][j] = c[i][j-1] b[i][j] = 2 # 2表示不在公共子序列但与0不同 return b and c def PrintLCS(b, X, i, j): #打印重复部分 if i == 0 or j == 0: return 0 if b[i][j] == 1: PrintLCS(b, X, i-1, j-1) print(X[i-1]) #注意二维列表中的i、j与X、Y序列(列表)差1 elif b[i][j] == 0: PrintLCS(b, X, i-1, j) else: PrintLCS(b, X, i, j-1) if __name__ == '__main__': X = list(input()) Y = list(input()) print(X) print(Y) m = len(X) n = len(Y) c = [[] for i in range(m+1)] for i in range(m+1): for j in range(n+1): c[i].append(0) b = copy.deepcopy(c) DPLength(X, Y, c, b) PrintLCS(b, X, m, n) print(c) print("X在Y中的重复率达到:", c[m][n]/n*100, "%") print("Y在X中的重复率达到:", c[m][n]/m*100, "%")
五、实验结果
输入:
X:ABCBDAB
Y: BDCABA
输出:
ABCBDAB
BDCABA
['A', 'B', 'C', 'B', 'D', 'A', 'B']