动态规划之最长公共子序列
参考资料
1 算法导论
2 v_JULY_v
3
何海涛
最长公共子序列(Longest Common Subsequence)
给定两个序列 X 和 Y ,称序列 Z 是 X 和 Y 的公共子序列,如果 Z 即是 X 的一个子序列又是 Y 的一个子序列, 该公共子序列不要求是连续的,只是要求Z的元素在X和Y中是以相同的顺序出现。例如,如果 X = { A , B , C , B , D , A , B } ,Y = { B , D , C , A , B , A } ,则序列 { B , C , B , A } 即为 X 和 Y 的一个公共子序列。序列 { B , C, A } 不是 X 和 Y 的一个最长公共子序列,因为长度只是3 ,不是最长的。
递归解
在找X= ( x1,⋯,xm )和 Y = ( y1,⋯,yn ) 的一个 LCS时,需要检查一个或两个子问题。如果 xm = yn,必须找出 xm-1 和 yn-1的一个LCS。将 xm = yn 添加到这个 LCS上,可以产生 X 和 Y 的一个 LCS 。如果 xm != yn ,就必须解决两个子问题,找出xm-1和Y 的一个LCS,以及找出 X 和 yn-1 的一个LCS 。这两个LCS中,较长的就是 X和 Y 的一个 LCS 。
所以 LCS的递归式如下:
计算 LCS 的长度
LCS-LENGTH ( X , Y)
m ← length[ X ]
n ← length[ Y ]
for i ← 1 to m
do c[i,0] ← 0
for j ← 0 to n
do c[0,j] ← 0
for i ← 1 to m
do for j ← 1 to n
do if xi = yj
then c[i,j] ← c[i-1,j-1] +1
b[i,j] ← “ \”
else if c[i-1,j] > c[i,j-1]
then c[i,j] ← c[i-1,j]
b[i,j] ← " |"
else c[i,j] ← c[i,j-1]
b[i,j] ← “---”
return c and b
构造一个LCS
PRINT-LCS(b,X,i,j)
if i=0 or j=0
then return;
if b[i,j]="\"
then LCS(b,X,i-1,j-1);
print(x[i]);
else if b[i,j]="|"
then LCS(b,X,i-1,j)
else LCS(b,X,i,j-1);
代码实现
#include "string.h" // directions of LCS generation enum decreaseDir {kInit = 0, kLeft, kUp, kLeftUp}; / // Get the length of two strings' LCSs, and print one of the LCSs // Input: pStr1 - the first string // pStr2 - the second string // Output: the length of two strings' LCSs / int LCS(char* pStr1, char* pStr2) { if(!pStr1 || !pStr2) return 0; size_t length1 = strlen(pStr1); size_t length2 = strlen(pStr2); if(!length1 || !length2) return 0; size_t i, j; // initiate the length matrix int **LCS_length; LCS_length = (int**)(new int[length1]); for(i = 0; i < length1; ++ i) LCS_length[i] = (int*)new int[length2]; for(i = 0; i < length1; ++ i) for(j = 0; j < length2; ++ j) LCS_length[i][j] = 0; // initiate the direction matrix int **LCS_direction; LCS_direction = (int**)(new int[length1]); for( i = 0; i < length1; ++ i) LCS_direction[i] = (int*)new int[length2]; for(i = 0; i < length1; ++ i) for(j = 0; j < length2; ++ j) LCS_direction[i][j] = kInit; for(i = 0; i < length1; ++ i) { for(j = 0; j < length2; ++ j) { if(i == 0 || j == 0) { if(pStr1[i] == pStr2[j]) { LCS_length[i][j] = 1; LCS_direction[i][j] = kLeftUp; } else LCS_length[i][j] = 0; } // a char of LCS is found, // it comes from the left up entry in the direction matrix else if(pStr1[i] == pStr2[j]) { LCS_length[i][j] = LCS_length[i - 1][j - 1] + 1; LCS_direction[i][j] = kLeftUp; } // it comes from the up entry in the direction matrix else if(LCS_length[i - 1][j] > LCS_length[i][j - 1]) { LCS_length[i][j] = LCS_length[i - 1][j]; LCS_direction[i][j] = kUp; } // it comes from the left entry in the direction matrix else { LCS_length[i][j] = LCS_length[i][j - 1]; LCS_direction[i][j] = kLeft; } } } LCS_Print(LCS_direction, pStr1, pStr2, length1 - 1, length2 - 1); for(i = 0; i < length1; i++) { delete [] LCS_length[i]; delete [] LCS_direction[i]; } return LCS_length[length1 - 1][length2 - 1]; } / // Print a LCS for two strings // Input: LCS_direction - a 2d matrix which records the direction of // LCS generation // pStr1 - the first string // pStr2 - the second string // row - the row index in the matrix LCS_direction // col - the column index in the matrix LCS_direction / void LCS_Print(int **LCS_direction, char* pStr1, char* pStr2, size_t row, size_t col) { if(pStr1 == NULL || pStr2 == NULL) return; size_t length1 = strlen(pStr1); size_t length2 = strlen(pStr2); if(length1 == 0 || length2 == 0 || !(row < length1 && col < length2)) return; // kLeftUp implies a char in the LCS is found if(LCS_direction[row][col] == kLeftUp) { if(row > 0 && col > 0) LCS_Print(LCS_direction, pStr1, pStr2, row - 1, col - 1); // print the char printf("%c", pStr1[row]); } else if(LCS_direction[row][col] == kLeft) { // move to the left entry in the direction matrix if(col > 0) LCS_Print(LCS_direction, pStr1, pStr2, row, col - 1); } else if(LCS_direction[row][col] == kUp) { // move to the up entry in the direction matrix if(row > 0) LCS_Print(LCS_direction, pStr1, pStr2, row - 1, col); } }