动态规划
前面一篇博文动态规划 矩阵连乘问题,学习了什么是动态规划,以及什么时候该用动态规划,总得来说就是:
当一个问题可以被分成若干个子问题求解,且子问题可以优化子结构,存在子问题重复的时候,就可以使用动态子结构。
最长公共子序列问题
最长公共子序列的定义:
我们来考虑一下如何分为子问题
设 两个序列分别为 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;
}
结果如下: