动态规划解决LCS问题

1. 问题定义:
给定两个序列 X = {x1, x2, ……, xm } 和 Y = {y1, y2, ……, yn },找出 X 和 Y 的最长公共子序列。


一个给定序列的子序列是在该序列中删去若干个元素后得到的序列。给定两个序列 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, A }是 X 和 Y 的一个公共子序列,序列{B, C, B, A }也是 X 和 Y 的一个公共子序列,且为最长公共子序列。


需要注意的是:最长公共子序列与最长公共子串之间的区别。
最长公共子串在原序列中是连续,而最长公共子序列中的各个元素在原序列中不需要连续。如字符串abdewefd和aeffdewsd最长公共子串为dew,而最长公共子序列为adewd。


2. 动态规划求解LCS问题
分析最长公共子序列的定义,从《算法导论》上有
定理:LCS的最优子结构性质

设序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,则:

  1. 若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列;

  2. 若xm≠yn且zk≠xm则Z是Xm-1和Y的最长公共子序列;

  3. 若xm≠yn且zk≠yn,则Z是X和Yn-1的最长公共子序列。

其中Xm-1=<x1, x2, …, xm-1>,Yn-1=<y1, y2, …, yn-1>,Zk-1=<z1, z2, …, zk-1>。


由最长公共子序列问题的最优子结构性质可知,要找出X=, x<x12, …, xm>和Y=<y1, y2, …, yn>的最长公共子序列,可按以下方式递归地进行:
当xm=yn时,找出Xm-1和Yn-1的最长公共子序列,然后在其尾部加上xm(=yn)即可得X和Y的一个最长公共子序列。
当xm≠yn时,必须解两个子问题,即找出Xm-1和Y的一个最长公共子序列及X和Yn-1的一个最长公共子序列。这两个公共子序列中较长者即为X和Y的一个最长公共子序列。

由此递归结构容易看到最长公共子序列问题具有子问题重叠性质。例如,在计算X和Y的最长公共子序列时,可能要计算出X和Yn-1Xm-1和Y的最长公共子序列。而这两个子问题都包含一个公共子问题,即计算Xm-1和Yn-1的最长公共子序列。

为了求解LCS问题,需要建立子问题最优值的递归关系。用c[i,j]记录序列Xi和Yj的最长公共子序列的长度。其中Xi=<x1, x2, …, xi>,Yj=<y1, y2, …, yj>。
当i=0或j=0时,空序列是Xi和Yj的最长公共子序列,故c[i,j]=0。
其他情况下,由定理可建立递归关系如下:

《动态规划解决LCS问题》



3. 代码实现
#include <iostream>
#include <string>
#include <stdlib.h>


typedef enum _eDir
{

LEFT_UP = 0,

UP,

LEFT,
}eDir;

void PrintLCS(const char* pStr, int length1, int length2, int** b)
{

if(pStr == NULL || b == NULL || length1 <=0 || length2 <= 0)

return;

int i=length1, j=length2;

if(b[i][j] == LEFT_UP)

{

PrintLCS(b, pStr, i-1, j-1);

printf(“%c “, pStr[i-1]);

}

else if(b[i][j] == UP)

PrintLCS(b, pStr, i-1, j);

else

PrintLCS(b, pStr, i, j-1);

}

void LCS_Length(const char* pStr1, const char* pStr2)
{
if (pStr1 == NULL || pStr2 == NULL)

throw new std::exception(“Invalid Parameter!”);

int str1Length = strlen(pStr1);

int str2Length = strlen(pStr2);

if(str1Length == 0 || str2Length == 0)

return;

int** c = (int**)new int[str1Length+1];

int** b = (int**)new int[str1Length+1];

for (int i=0; i<str1Length+1; ++i)

{
c[i] = new int[str2Length+1];

b[i] = new int[str2Length+1];

memset(c[i], 0, str2Length+1);

memset(b[i], 0, str2Length+1);

}

for (int i=1; i<=str1Length; ++i)

for (int j=1; j<=str2Length; ++j)

{

if (pStr1[i-1] == pStr2[j-1])

{

c[i][j] = c[i-1][j-1] + 1;

b[i][j] = LEFT_UP;

}

else if(c[i-1][j] >= c[i][j-1])

{

c[i][j] = c[i-1][j];

b[i][j] = UP;

}

else

{

c[i][j] = c[i][j-1];

b[i][j] = LEFT;

}

}

std::cout<<“str1:”<<pStr1<<std::endl<<“str2:”<<pStr2<<std::endl;

PrintLCS(pStr1, str1Length, str2Length, b);

std::cout<<std::endl;

for(int i=0; i<str1Length+1; ++i)

{

delete[] c[i];

delete[] b[i];

}

delete[] c;

delete[] b;

}


int main()

{

char* pStr1 = “ABCBDAB”;

char* pStr2 = “BDCABAB”;

LCS_Length(pStr1, pStr2);

return 0;

}


程序输出:
str1:ABCBDAB
str2:BDCABAB

B C B A B

点赞