先介绍
LCS
问题的性质:记
Xm={x0, x1,
…
xm-1}
和
Yn={y0,y1,
…
,yn-1}
为两个字符串,而
Zk={z0,z1,
…
zk-1}
是它们的
LCS
,则:
1. 如果 xm-1=yn-1 ,那么 zk-1=xm-1=yn-1 ,并且 Zk-1 是 Xm-1 和 Yn-1 的 LCS ;
2. 如果 xm-1 ≠ yn-1 ,那么当 zk-1 ≠ xm-1 时 Z 是 Xm-1 和 Y 的 LCS ;
3. 如果 xm-1 ≠ yn-1 ,那么当 zk-1 ≠ yn-1 时 Z 是 Yn-1 和 X 的 LCS ;
下面简单证明一下这些性质:
1. 如果 zk-1 ≠ xm-1 ,那么我们可以把 xm-1 ( yn-1 )加到 Z 中得到 Z ’ ,这样就得到 X 和 Y 的一个长度为 k+1 的公共子串 Z ’ 。这就与长度为 k 的 Z 是 X 和 Y 的 LCS 相矛盾了。因此一定有 zk-1=xm-1=yn-1 。
既然 zk-1=xm-1=yn-1 ,那如果我们删除 zk-1 ( xm-1 、 yn-1 )得到的 Zk-1 , Xm-1 和 Yn-1 ,显然 Zk-1 是 Xm-1 和 Yn-1 的一个公共子串,现在我们证明 Zk-1 是 Xm-1 和 Yn-1 的 LCS 。用反证法不难证明。假设有 Xm-1 和 Yn-1 有一个长度超过 k-1 的公共子串 W ,那么我们把加到 W 中得到 W ’ ,那 W ’ 就是 X 和 Y 的公共子串,并且长度超过 k ,这就和已知条件相矛盾了。
2. 还是用反证法证明。假设 Z 不是 Xm-1 和 Y 的 LCS ,则存在一个长度超过 k 的 W 是 Xm-1 和 Y 的 LCS ,那 W 肯定也 X 和 Y 的公共子串,而已知条件中 X 和 Y 的公共子串的最大长度为 k 。矛盾。
3. 证明同 2 。
有了上面的性质,我们可以得出如下的思路:求两字符串 Xm={x0,x1, … xm-1} 和 Yn={y0,y1, … ,yn-1} 的 LCS ,如果 xm-1=yn-1 ,那么只需求得 Xm-1 和 Yn-1 的 LCS ,并在其后添加 xm-1 ( yn-1 )即可;如果 xm-1 ≠ yn-1 ,我们分别求得 Xm-1 和 Y 的 LCS 和 Yn-1 和 X 的 LCS ,并且这两个 LCS 中较长的一个为 X 和 Y 的 LCS 。
如果我们记字符串 Xi 和 Yj 的 LCS 的长度为 c[i,j] ,我们可以递归地求 c[i,j] :
/ 0 if i<0 or j<0
c[i,j]= c[i-1,j-1]+1 ifi,j>=0 and xi=xj
\ max(c[i,j-1],c[i-1,j] ifi,j>=0 and xi ≠ xj
1. 如果 xm-1=yn-1 ,那么 zk-1=xm-1=yn-1 ,并且 Zk-1 是 Xm-1 和 Yn-1 的 LCS ;
2. 如果 xm-1 ≠ yn-1 ,那么当 zk-1 ≠ xm-1 时 Z 是 Xm-1 和 Y 的 LCS ;
3. 如果 xm-1 ≠ yn-1 ,那么当 zk-1 ≠ yn-1 时 Z 是 Yn-1 和 X 的 LCS ;
下面简单证明一下这些性质:
1. 如果 zk-1 ≠ xm-1 ,那么我们可以把 xm-1 ( yn-1 )加到 Z 中得到 Z ’ ,这样就得到 X 和 Y 的一个长度为 k+1 的公共子串 Z ’ 。这就与长度为 k 的 Z 是 X 和 Y 的 LCS 相矛盾了。因此一定有 zk-1=xm-1=yn-1 。
既然 zk-1=xm-1=yn-1 ,那如果我们删除 zk-1 ( xm-1 、 yn-1 )得到的 Zk-1 , Xm-1 和 Yn-1 ,显然 Zk-1 是 Xm-1 和 Yn-1 的一个公共子串,现在我们证明 Zk-1 是 Xm-1 和 Yn-1 的 LCS 。用反证法不难证明。假设有 Xm-1 和 Yn-1 有一个长度超过 k-1 的公共子串 W ,那么我们把加到 W 中得到 W ’ ,那 W ’ 就是 X 和 Y 的公共子串,并且长度超过 k ,这就和已知条件相矛盾了。
2. 还是用反证法证明。假设 Z 不是 Xm-1 和 Y 的 LCS ,则存在一个长度超过 k 的 W 是 Xm-1 和 Y 的 LCS ,那 W 肯定也 X 和 Y 的公共子串,而已知条件中 X 和 Y 的公共子串的最大长度为 k 。矛盾。
3. 证明同 2 。
有了上面的性质,我们可以得出如下的思路:求两字符串 Xm={x0,x1, … xm-1} 和 Yn={y0,y1, … ,yn-1} 的 LCS ,如果 xm-1=yn-1 ,那么只需求得 Xm-1 和 Yn-1 的 LCS ,并在其后添加 xm-1 ( yn-1 )即可;如果 xm-1 ≠ yn-1 ,我们分别求得 Xm-1 和 Y 的 LCS 和 Yn-1 和 X 的 LCS ,并且这两个 LCS 中较长的一个为 X 和 Y 的 LCS 。
如果我们记字符串 Xi 和 Yj 的 LCS 的长度为 c[i,j] ,我们可以递归地求 c[i,j] :
/ 0 if i<0 or j<0
c[i,j]= c[i-1,j-1]+1 ifi,j>=0 and xi=xj
\ max(c[i,j-1],c[i-1,j] ifi,j>=0 and xi ≠ xj
上面的公式用递归函数不难求得。
#include <string.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
// directions of LCS generation
enum decreaseDir
{
kInit = 0, kLeft, kUp, kLeftUp
};
void LCS_Print(int **LCS_direction, char* pStr1, char* pStr2, size_t row,
size_t col);
/
// 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++)
{
printf("\n");
for (j = 0; j < length2; j++)
{
printf("%d%s\t", LCS_length[i][j],
(LCS_direction[i][j] == kUp ?
"↑" : (LCS_direction[i][j] == kLeft ? "←" :
(LCS_direction[i][j] == kLeftUp ? "↖" : " "))));
}
}*/
int len = LCS_length[length1 - 1][length2 - 1];
for (i = 0; i < length1; ++i)
delete[] LCS_length[i];
delete[] LCS_length;
return len;
}
/
// 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);
}
}
int main(void)
{
LCS("ABCBDAB", "BDCABA");//结果:BDAB
return 0;
}