算法导论学习笔记(十二):动态规划(二):最长公共子序列(LCS)

LCS:给出两个序列S1和S2,求出的这两个序列的最大公共部分S3就是就是S1和S2的最长公共子序列了。公共部分

须是以相同的顺序出现,但是不必要是连续的。


解法一:在没学动态规划之前,我能想到的方法就是枚举了。将S1的所有子序列全部检查是否是S2的子序列,从中

出最长公共子序列。对于长度为n的序列,其子序列共有2的n次方个,这样的话这种算法的时间复杂度就为指数级

了,这显然不太适合用于序列很长的求解了。


解法二:既然学到了动态规划,就来看看能否用动态规划的思想来解决这个问题。要使用动态规划,必须满足两个条

件:有最优子结构和重叠子问题。为了便于学习,我们先来了解下这两个概念。

如果问题的一个最优解中包含了子问题的最优解,则该问题具有最优子结构。当一个递归算法不断地调用同一问题

时,我们说该最优问题包含重叠子问题。

说明:动态规划要求其子问题既要独立又要重叠,这初看上去貌似存在矛盾,其实不然。只是这里说的是两个不同的

念。独立指的是同一问题的两个子问题不共享资源。而重叠子问题指的是子问题可以作为不同的问题的子问题出

现。

设X = <x1,x2...xm>, Y = <y1,y2...yn>为两个序列,并设Z = <z1,z2...zk>为X和Y的任意一个LCS。可以得出:

1、如果xm = yn,那么zk = xm = yn而且Z(k-1)是X(m-1)和Y(n-1)的一个LCS;

2、如果xm != yn,那么zk != xm蕴含Z是X(m-1)和Y的一个LCS;

3、如果xm != yn,那么zk != yn蕴含Z是X和Y(n-1)的一个LCS。

注:上面的Z(k-1)表示序列Z<z1,z2...zn>,其中n=k-1。其它的X()和Y()也是一样的。

很容易证明上述三点是成立的,详细证明见算法导论。所以LCS具有最优子结构。从上面也可以看出LCS问题中的重

叠子问题的性质。所以我们可以用动态规划来解决LCS问题。由LCS问题的最优子结构可得出递归式:


下面来看看实现代码:

#include<iostream>
#include<cstring>
using namespace std;

int c[100][100]; // c[i][j]表示序列S1前i个元素和S2的前j个元素的LCS
int b[100][100]; //便于求解最优解

void LCS_Length (char x[], char y[]);
void Print_LCS (char x[], int i, int j);

int main()
{
	char X[100], Y[100];

	while (cin >> X >> Y)
	{
		LCS_Length (X, Y);
		Print_LCS (X, strlen(X), strlen(Y));
		cout << endl;
	}
	return 0;
}

void LCS_Length (char x[], char y[])
{
	int m = strlen (x);
	int n = strlen (y);
	for (int i = 0; i <= m; i++)
		c[i][0] = 0;
	for (int i = 0; i <= n; i++)
		c[0][i] = 0;

	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (x[i] == y[j])
			{
				c[i + 1][j + 1] = c[i][j] + 1;
				b[i + 1][j + 1] = 0;
			}
			else if (c[i][j + 1] >= c[i + 1][j])
			{
				c[i + 1][j + 1] = c[i][j + 1];
				b[i + 1][j + 1] = 1;
			}
			else
			{
				c[i + 1][j + 1] = c[i + 1][j];
				b[i + 1][j + 1] = 2;			
			}
		}
	}
}

void Print_LCS (char x[], int i, int j)
{
	if ((i == 0) || (j == 0))
		return ;
	if (b[i][j] == 0)
	{
		Print_LCS (x, i - 1, j - 1);
		cout << x[i - 1] << ' ';
	}
	else if (b[i][j] == 1)
		Print_LCS (x, i -1, j);
	else
		Print_LCS (x, i, j - 1);
}

书上后面给出了一种可以节省空间的方法,就是不要b数组,直接判断。因为c[i][j]仅依赖于c[i-1][i-1],c[i-1][j],c[i][j-1]

这样的话只要小小修改下Print_LCS函数即可。


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值