您的位置:首页 > 其它

动态规划 最常公共子序列问题

2016-12-05 13:04 141 查看

动态规划

前面一篇博文动态规划 矩阵连乘问题,学习了什么是动态规划,以及什么时候该用动态规划,总得来说就是:

当一个问题可以被分成若干个子问题求解,且子问题可以优化子结构,存在子问题重复的时候,就可以使用动态子结构。

最长公共子序列问题

最长公共子序列的定义:



我们来考虑一下如何分为子问题

设 两个序列分别为 X = {x1, x2, x3 …….xm}, Y = {y1, y2, y3 ……yn},他们的最长公共子序列为 Z = {z1, z2, z3 ….zk},那么就存在下面这几种情况:

如果xm == yn == zk , 那么 Z = {z1, z2…..zk-1} 一定是 X = {x1, x2, x3 …….xm-1}, Y = {y1, y2, y3 ……yn-1}的最长公共子序列;

如果xm != yn, 且zk != xm, 那么Z = {z1, z2…..zk} 是 X = {x1, x2, x3 …….xm-1}, Y = {y1, y2, y3 ……yn} 的最长公共子序列;

如果xm != yn, 且zk != yn, 那么Z = {z1, z2…..zk} 是 X = {x1, x2, x3 …….xm}, Y = {y1, y2, y3 ……yn-1} 的最长公共子序列;

并且我们可以看到存在子问题重叠的问题



如果不太理解,可以具体代入。

这样的话,当我们问题如果遇到xm == yn的情况下,就直接解决了一个子问题,说明xm或者yn就是最长公共子序列中的最后一个,接下来我们就只需要求X = {x1, x2, x3 …….xm-1}, Y = {y1, y2, y3 ……yn-1}的序列;当xm != yn的情况下,则需要解决两个子问题,分别是zk != xm和 zk != yn的情况,然后取两种情况分别所得的最长公共子序列的最大致。

概念如果有点模糊的话,我们来举个例子:

1. 如果X = “ABCD”,Y = “ACBD”,由于xm = “D” 和 yn = “D”相等,所以我们已经得出XY的最大公共子序列的最后一个值是D;

2. 如果X = “ABDC”,Y = “ACBD”,由于xm = “C”和 yn = “D”不相等,所以我们要解决X = “ABD”,Y = “ACBD”和 X = “ABDC”,Y = “ACB”两个子问题,取两个子问题中最长的公共子序列。

递归方程



输出最长公共子序列

除了用c[i, j]来记录i到j的最长公共子序列的长度,还要用一个数组b[i][j]来记录每一个子问题的前一个子问题,然后回溯输出最长公共子序列。



(图从网上摘得,如果作者不允许使用,请告知)

代码实现

#include<cstdio>
#include<cstring>

using namespace std;

const int MAXLEN = 10000;

int b[MAXLEN][MAXLEN];
int c[MAXLEN][MAXLEN];

void LCSLength(char* x, char* y, int m, int n){
//初始化0边界
int i;
for(i = 0; i <= m; i++){
c[i][0] = 0;
}
for(i = 1; i <= n; i++){
c[0][i] = 0;
}
for(i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
//用0表示左上,1表示左,2表示上
if(x[i-1] == y[j-1]){
c[i][j] = c[i-1][j-1] + 1;
b[i][j] = 0;
}else if(c[i-1][j] > c[i][j-1]){
c[i][j] = c[i-1][j];
b[i][j] = 1;
}else{
c[i][j] = c[i][j-1];
b[i][j] = 2;
}
}
}

//直观看看c数组和b数组
for(i = 0; i < m; i++){
for(int j = 0; j < n; j++){
printf("%d ", b[i][j]);
}
printf("\n");
}
printf("\n");
for(i = 0; i < m; i++){
for(int j = 0; j < n; j++){
printf("%d ", c[i][j]);
}
printf("\n");
}
printf("\n");
}

void printLCS(char *x, int i, int j)
{
if(i == 0 || j == 0)
return;
if(b[i][j] == 0)
{
printLCS(x, i-1, j-1);
printf("%c ", x[i-1]);
}
else if(b[i][j] == 1)
printLCS(x, i-1, j);
else
printLCS(x, i, j-1);
}

int main(){
char x[MAXLEN] = {"ABCEOG"};
char y[MAXLEN] = {"BAEOGA"};

int m = strlen(x);
int n = strlen(y);

LCSLength(x, y, m, n);
printLCS(x, m, n);
return 0;
}


结果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: