问题:给定两个字符串,找出它们的最长公共子序列。
首先了解题目,子序列和字串是不同的。在字符串匹配里,子串通常指的是给定字符串的一部分,是连续的不可断开的。而子序列是不同的,是在给定字符串里,按照顺序取字符,可以连续可以断开,然后组合构成新的字符串。通常子序列都不是给定字符串的子串,但是子串也可以称为子序列。另外公共子序列不一定是最长公共子序列的子串。
不同的取字符方式可以构建出相同的子序列,如上图子序列和子序列2。而当给定两个字符串,公共子序列代表可以从两个字符串里按顺序抽取字符并组成新的序列,且新的序列相同,最长公共子序列则代表能用这种方式取出最长的相同序列。
生物学上的DNA匹配其实就是在寻找最长公共子序列。比如 S 1 = A C C G G A T C C G , S 2 = C C G A T C G C G C C G S_1=ACCGGATCCG, S_2=CCGATCGCGCCG S1=ACCGGATCCG,S2=CCGATCGCGCCG,可以找到它们的最长公共子序列为 C C G A T C C G CCGATCCG CCGATCCG。如果用暴力解法来解决这个问题,需要对检查两条字符串的所有子序列,效率很低。如果想找到更快的解法,需要研究一下公共子序列的性质,即它与两条给定字符串有什么关系,为了方便,称给定字符串为X和Y,它们的最长公共子序列为Z。
- 如果X和Y的最后一个元素相同,那么它们的最后一个元素也是Z的最后一个元素。若将X,Y和Z的最后一个元素全部去掉,得到X’,Y’,Z’,Z’依旧是X’和Y’的最长公共子序列。
- 如果X和Y的最后一个元素不同,如果Z的最后一个元素和X的最后一个元素不同,那么把X的最后一个元素去掉得到X’,那么Z依旧是X’和Y的最长公共子序列。
- 如果X和Y的最后一个元素不同,如果Z的最后一个元素和Y的最后一个元素不同,那么把Y的最后一个元素去掉得到Y’,那么Z依旧是X和Y‘的最长公共子序列。
这几条性质比较好理解。其中2和3是对称的。如果一个给定字符串里的最后一个元素不能放在公共子序列里, 那么这个字符串的搜索范围可以缩小,这也是用动态规划解这道题的部分基础:如果X和Y的最末元素相同,那么子问题是在X’和Y’里查找最长公共子序列(找到后需要将最末公共元素加上); 如果X和Y的最末元素不同,则产生了两个子问题:1. 查找X’和Y的最长公共子序列。2. 查找X和Y’的最长公共子序列(找到后需要比较两个子序列并取最长)。
用递归公式来表示如下:
c [ i , j ] = { 0 i = 0 或 j = 0 c [ i − 1 , j − 1 ] + 1 i , j > 0 且 x i = y i m a x ( c [ i , j − 1 ] , c [ i − 1 , j ] ) i , j > 0 且 x i ≠ y i . c[i,j]= \begin{cases} 0 & & {i=0 或 j=0}\\ c[i-1,j-1]+1 & & {i, j>0 且 x_i=y_i}\\ max(c[i,j-1], c[i-1,j]) & & {i, j>0 且 x_i\neq y_i} \end{cases} . c[i,j]=⎩⎪⎨⎪⎧0c[i−1,j−1]+1max(c[i,j−1],c[i−1,j])i=0或j=0i,j>0且x