动态规划:最长公共子序列问题
动态规划的一般步骤:
-
问题结构分析
给出两个序列X[n],Y[m],求X[n],Y[m]的最长公共子序列的长度C[n][m] -
递推关系建立
用一个具体的例子来分析序列末尾字符:情况1:X[n],Y[m]末尾字符不同
情况2:X[n],Y[m]末尾字符相同
先来看情况1:X[n],Y[m]末尾字符不同,情况1有两种可能:
(1)去掉Y[m]末尾字符,即C[7][6-1]+0:
(2)去掉X[m]末尾字符,即C[7-1][6]+0:
所以递归关系为:C[i,j]= max { C[i -1,j], C[i,j-1] }
再来看情况2:X[n],Y[m]末尾字符相同,情况2有三种可能:
(1)X[n],Y[m]末尾字符同时选取,并隐去后,下一步的得C[7-1,6-1] + 1
但两者并不一定都出现在最长子序列中,所以就有了(2)(3)两种可能:
(2)去掉Y[m]末尾字符,即C[7][6-1]+0:
(3)去掉X[m]末尾字符,即C[7-1][6]+0:
但这三种可能并不是需要都求解,分析得C[i-1,j-1]+1 >= max{C[i,j -1],C[i-1,j],所以最终的递归关系为:C[i,j]= C[i -1,j-1]+1 -
自底向上计算
(1)初始化:C[i,0]= C[0,j]= 0;某序列长度为0时,最长公共子序列长度为0
(2)递推公式:
-
最优方案追踪
构造追踪数组rec[n],记录子问题来源
算法图解
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
string X,Y;
int C[1001][1001];//记录公共子序列长度
int main(){
cin >> X >> Y;
int n = X.length(), m = Y.length();
//初始化
for(int i=0; i<n; i++)
C[i][0] = 0;
for(int j=0; j<n; j++)
C[0][j] = 0;
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
if(X[i]==Y[j])
C[i][j] = C[i-1][j-1]+1;
else
C[i][j] = max(C[i][j-1],C[i-1][j]);
}
}
cout << C[n][m] << endl;//输出最长公共子序列长度
return 0;
}