动态规划详解之——最长的公共子序列

最长公共子序列

  • 问题分析
    • 分析最优解的结构特征
    • 建立最优值的递归式
    • 底向上计算最优值,并记录最优值和最优策略
    • 构造最优解
  • 算法设计
  • 完美图解
  • 伪代码详解
  • 完整代码
  • 相关题解

问题分析

给定两个序列X={x1,x2,…,xm} 和 Y={y1,y2,…,yn},找出X和Y的一个最长的公共子序列。
这里的讲解是参照趣学算法一书,此书的地址在Dijkstra的文章末。

分析最优解的结构特征

假设已经知道 Zk={z1,z2,…,zk} 是X={x1,x2,…,xm} 和 Y={y1,y2,…,yn的最长公共子序列。那么可以分3中情况讨论。
Xm=Yn=Zk 那么Zk-1={z1,z2,…,zk-1}是 Xm-1 和 Yn-1的最长公共子序列
Xm!=Yn Xm!=Zk 我们可以 把Xm去掉,那么Zk是Xm-1和Yn的最长公共子序列
Yn!=Xm Yn!=Zk 同上理,Xm和Yn-1

建立最优值的递归式

  • Xm=Yn=Zk: 那么c[i][j]=c[i-1][j-1]+
  • Xm != Yn: 那么 c[i][j]=max{c[i][j-1],c[i-1][j]}
    所以递归式为:动态规划详解之——最长的公共子序列_第1张图片

底向上计算最优值,并记录最优值和最优策略

动态规划详解之——最长的公共子序列_第2张图片

构造最优解

上面的求解过程只是得到了最长公共子序列的长度,并不知道最长公共子序列是什么。
这里比较重要,我就直接贴图了,这样看清楚点。
动态规划详解之——最长的公共子序列_第3张图片

算法设计

(1)确定合适的数据结构
	采用二维数组c[][]来记录最长公共子序列的长度,二维数组b[][]来记录最长公共子序列的长度来源
(2)初始化
	输入两个字符串s1,s2,初始化c[][]第一行第一列为0
(3)循环阶段

动态规划详解之——最长的公共子序列_第4张图片

(4)构造最优解

动态规划详解之——最长的公共子序列_第5张图片

完美图解

动态规划详解之——最长的公共子序列_第6张图片动态规划详解之——最长的公共子序列_第7张图片动态规划详解之——最长的公共子序列_第8张图片动态规划详解之——最长的公共子序列_第9张图片动态规划详解之——最长的公共子序列_第10张图片

伪代码详解

(1)最长公共子序列求解函数
	首先计算两个字符串长度,然后从i=1开始,s1[0]与s2中的每一个字符比较
void LCSL(){
	for(int i=1;i<=len1;i++){//控制s1序列	
		for(int j=1;j<=len2;j++){//控制s2序列 
			if(s1[i-1]==s2[j-1]){//小标从0开始
				c[i][j]=c[i-1][j-1]+1;
				b[i][j]=1;
			}
			else{
				if(c[i][j]>=c[i-1][j]){
					c[i][j]=c[i][j-1];
					b[i][j]=2;
				}
				else{
					c[i][j]=c[i-1][j];
					b[i][j]=3;
				}
			}
		}
	}
}

(2)最优解输出函数
void print(int i,int j){
	if(i==0 || j==0) return ;
	if(b[i][j]==1){
		print(i-1,j-1);
		cout<<s1[i-1];
	}
	else if(b[i][j==2])
		print(i,j-1);
	else print(i-1,j);
}

完整代码

#include 
#include 
using namespace std;
const int N = 1024;
int c[N][N], b[N][N];
char s1[N], s2[N];
int len1, len2;
void LCS()
{
	for (int i = 1; i <= len1; i++) {
		for (int j = 1; j <= len2; j++) {
			if (s1[i - 1] == s2[j - 1]) { //注:此处的s1与s2序列是从s1[0]与s2[0]开始的
				c[i][j] = c[i - 1][j - 1] + 1;
				b[i][j] = 1;
			}
			else {
				if (c[i][j - 1] >= c[i - 1][j]) {
					c[i][j] = c[i][j - 1];
					b[i][j] = 2;
				}
				else {
					c[i][j] = c[i - 1][j];
					b[i][j] = 3;
				}
			}
		}
	}
}

void LCS_PRINT(int i, int j)
{
	if (i == 0 || j == 0) {
		return;
	}
	if (b[i][j] == 1) {
		LCS_PRINT(i - 1, j - 1);
		cout << s1[i - 1];
	}
	else if (b[i][j] == 2) {
		LCS_PRINT(i, j - 1);
	}
	else {
		LCS_PRINT(i - 1, j);
	}
}

int main()
{
	cout << "请输入X字符串" << endl;
	cin >> s1;
	cout << "请输入Y字符串" << endl;
	cin >> s2;
	len1 = strlen(s1);
	len2 = strlen(s2);
	for (int i = 0; i <= len1; i++) {
		c[i][0] = 0;
	}
	for (int j = 0; j <= len2; j++) {
		c[0][j] = 0;
	}
	LCS();
	cout << "s1与s2的最长公共子序列的长度是:" << c[len1][len2] << endl;
	cout << "s1与s2的最长公共子序列是:";
	LCS_PRINT(len1, len2);
	return 0;
}

//输入、输出
	ABCADAB
	BACDBA
	输出
	4
	BADB

相关题解

你可能感兴趣的:(算法,动态规划,公共子序列,c++)