问题描述:Given 2 sequences, X = x1,...,xm and Y = y1,...,yn, find a common subsequence whose length is maximum. Subsequence need not be consecutive, but must be in order.
程序思路:
使用递归的思路可以解决这个问题。设输入的两个子串为X[0…m - 1]和Y[0…n - 1],L(X[0…m - 1], Y[0…n - 1])为X和Y的最长公共子串长度。分两种情况:
1. 若子串最后一个字符匹配(即X[m – 1] == Y[n – 1]),则
L(X[0…m - 1], Y[0…n - 1])= 1 + L(X[0…m - 2], Y[0…n - 2])
2. 若子串最后一个字符不匹配(即X[m – 1] != Y[n – 1]),则
L(X[0…m - 1], Y[0…n - 1]) = Max( L(X[0…m - 2], Y[0…n - 1]), L(X[0…m - 1], Y[0…n - 2]))
但是递归的情况下,很多子情况会重复计算多次,因此可以使用动态规划来优化,这里可以使用画表格的方法来记录下子问题的解,供后面计算使用。这里的表格记录最长公共子串的长度。
例如:”ABCFGR” 和”ACKWGR”的最长公共子串表为
右下角的值就是两个子串的最长公共子串长度。
如果需要输出两个子串的最长公共子串,则需要额外的空间把每个字符存起来。步骤如下:
- 构造最长公共子串长度表L[][];
- 构造一个长为L[m][n]的string s;
- 从L[m][n]这个位置开始遍历L[][],直到m或者n为0,对于L[][]中的每个元素L[i][j],
a) 若X[i - 1] == Y[j - 1],即这个字符是最长公共子串的一个字符,放在s的末尾,往左上方移动一格,即i – 1,j – 1,继续遍历。
b) 若不等,则在表格左边或者上方选择一个较大值,移动一格,即i – 1或者j – 1,继续遍历。
根据上面的例子,给出遍历路径:
所以”ABCFGR” 和”ACKWGR”的最长公共子串为”ACGR”。
算法伪代码:
//LCS算法
//输入:子串X,子串Y,X的长度m,Y的长度n
//输出:最长公共子串
LCS(X[], Y[], m, n)
Begin
Create L[m + 1][n + 1]
for each i from 0 to m do
for each j from 0 to n do
if i == 0 or j == 0
L[i][j] = 0
Else if X[i – 1] == Y[j – 1]
L[i][j] = L[i – 1][j – 1] + 1
Else
L[i][j] = max(L[i – 1][j], L[i][j – 1])
Create char c[L[m][n]]
For L[m][n] to L[0][p] or L[q][0]
If X[i - 1] == Y[j – 1]
Push this char to c
Skip to left up
Else
Skip to left or up which is larger
end
算法性能分析:
这个算法主要做的事情是,建立最长公共子串表,然后遍历这个表。表的建立需要一个一个填写表项,因此复杂度为O(mn)。