题目描述:给出两个字符串,找到最长公共子序列(LCS),返回LCS的长度。
说明:
最长公共子序列的定义:最长公共子序列问题是在一组序列(通常2个)中找到最长公共子序列(注意:不同于子串,LCS不需要是连续的子串)。该问题是典型的计算机科学问题,是文件差异比较程序的基础,在生物信息学中也有所应用。https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
样例:
给出"ABCD" 和 "EDCA",这个LCS是 "A" (或 D或C),返回1
给出 "ABCD" 和 "EACB",这个LCS是"AC"返回 2
什么是LCS,上面的定义已经很清楚了。我不赘述了。我们之前做的一道题是求两个字符串之间的不同的子序列(详见:点击打开链接),本题也是关于子序列的,所以两道题肯定有类似的地方。我们还是用动态规划(其实你见的多了就会立马有种感觉,知道这道题该用动态规划),用二维表格record[i][j]表示字符串A的前 i 项与字符串B的前 j 项的LCS的长度。
现在开始写状态转移方程。我们来分析A[i]与B[j]的关系对两个串的LCS的长度造成的影响。
1. 若A[i] == B[j],那么,很显然,A的前 i 项与B的前 j 项的LCS为A的前 i - 1项与B的前 j - 1项的LCS加上A[i]。比如,A = "bupt", B= "bupat"中,最后一项相同,除最后一项外,前面的项为A' = "bup", B' = "bupa",A'与B'的LCS为“bup”,所以A与B的LCS为“bup” + "t"
2. 若A[i] != B[j],这时候的情况就会比较复杂:
(1)若A[i]与B[j]谁都没有为A的前 i 项与B的前 j 项的LCS做出“贡献”,那A的前 i 项与B的前 j 项的LCS为A的前 i - 1项与B的前 j - 1项的LCS是一样的。比如,A = "bupta" 与 B = "bupec"的LCS还是A' = "bupt", B' = "bupe"的LCS
(2)若A[i]与B[j]有一个为A的前 i 项与B的前 j 项的LCS做出“贡献”,那A的前 i 项与B的前 j 项的LCS则必然与
<1>. A的前 i - 1项与B的前 j 项的LCS
<2>. A的前 i 项与B的前 j - 1项的LCS
有一个是相同的。比如,A = "bupte" 与 B = "bupec",A的最后一项"e"为A, B的LCS做出“贡献”了,那么LCS为A = "bupte" 与 B‘ = "bupe"的LCS是相同的,且长度一定大于A' = "bupt"与B = "bupec"的LCS。
因此,状态转移方程:
record[i][j] = record[i - 1][j - 1] + 1(A[i] == B[j])
record[i][j] = max(record[i - 1][j], record[i][j - 1])
初始条件与求两个字符串之间的不同的子序列的设定是类似的,都是先拓展这个二维表格,使得行,列各增加一个,这也是动态规划常用的方法,我就不多了,不理解的话返回上面给出的链接自己学习。
代码如下:
class Solution:
"""
@param A, B: Two strings.
@return: The length of longest common subsequence of A and B.
"""
def longestCommonSubsequence(self, A, B):
m = len(A)
n = len(B)
if m == 0 or n == 0:
return 0
record = [[0 for j in range(n + 1)] for i in range(m + 1)]
i = 1
while i <= m:
j = 1
while j <= n:
if A[i - 1] == B[j - 1]:
record[i][j] = record[i - 1][j - 1] + 1
else:
record[i][j] = max(record[i - 1][j], record[i][j - 1])
j += 1
i += 1
return record[m][n]
# write your code here
需要注意的是,LCS在数据挖掘中很很有用的方法,正如题目所描述的那样。我在以后会对判断两个文件的相似度做一个专题。