从最长公共子序列看动态规划

穷举法:

已知 X[1…m]的一个子串 X[i…j]检测是否 Y[1…n]的子串?
按照X,Y的下标递归扫描,看是否相同,扫描为 O(n)
X多少个子串?
2 m,我们可以化为二进制考虑,然后每一个用0和1分别表示是否在串中
这个算法整个运行时间?
不难看出O( 2 mn),显然不好因为指数级的

简化

上面我们是从检测每个子串是否位于另一个序列中,这个会运行时间随着原来串的长度不断增加,必须从一个方面想,比如从序列长度入手

最优子结构,一个问题的最优解包含了子问题的最优解

LCS(X,Y)存储在Z[1…k],那么Z[1…k]的任何一个前缀 是 X[1…m]的某个前缀和Y[1…n]的某个前缀的LCS

证明不说:与另一篇博文MST的最优子结构证明方法一致
得到下面公式

先规定一下符号
X[1…m],Y[1…n] 的最长公共子序列记为 LCS(X,Y)存储在 Z[1…k],然后 |LCS(X,Y)|表示最长公共子序列长度也就是 K
|LCS(i,j)| 的最长公共子序列长度为 L(i,j),也就是 |LCS(X,Y)|的最长公共子序列长度为 L(X,Y)

在这里插入图片描述

公式证明

在这里插入图片描述

自顶向下递归

int LCS(X,Y,i,j)
{
	if(i==0||j==0) return c[i,j]
	if(X[i]==Y[j]) {c[i,j]=LCS(X,Y,i-1,j-1)+1}
	else{
		c[i,j]=max(LCS(X,Y,i-1,j),LCS(X,Y,i,j-1)) 
	}	
	return c[i,j]	
}

最坏情况:全是第二种情况
在这里插入图片描述
树的高度最高:m+n,说明运算是指数阶的

重叠子问题

在这里插入图片描述
LCS只包含了mn个独立的子问题,所以需要找一个方法只需要计算mn个问题

改进:自顶向下备忘法

int LCS(X,Y,i,j)
{
	if(i==0||j==0) return c[i,j]
	if (c[i,j]==nil)
	{
		if(X[i]==Y[j]) {c[i,j]=LCS(X,Y,i-1,j-1)+1 //可以输出}
		else{
			c[i,j]=max(LCS(X,Y,i-1,j),LCS(X,Y,i,j-1)) 
		}
	}
	return c[i,j]	
}

运行时间:Θ(mn)
空间:Θ(mn)
运行时间分析:平摊分析

再改进:真动态规划,自顶向下备忘法计算表格

int LCS(X,Y,m,n)
{
	for(int i=1;i<=m;i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (X[i-1] == Y[j-1]) {
				c[i,j] = c[i - 1,j - 1] + 1;
			}
			else {
				c[i,j] = max(c[i,j - 1], c[i - 1,[j]);
			}
		}
	}
}

运行时间:Θ(mn)
空间:Θ(mn) 可以改进Θ({m,n})

使用回溯法获取

代码

#include <iostream>
using namespace std;
int max(int x, int y)
{
	if (x >= y)
		return x;
	else
		return y;
}


int c[8][7]; //需要大一圈
int LCS(char *X, char *Y, int i, int j) //自顶向下
{
	
	if (i == 0 || j == 0) c[i][j] = 0;
	if (c[i][j] == -1)
	{
		if (X[i-1] == Y[j-1]) {
			c[i][j] = LCS(X, Y, i - 1, j - 1) + 1; 
		}
		else {
			c[i][j] = max(LCS(X, Y, i - 1, j), LCS(X, Y, i, j - 1));
		}
	}
	return c[i][j];
}
void LCS1(char *X, char *Y, int m, int n) //自底向上计算表格
{

	for(int i=1;i<=m;i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (X[i-1] == Y[j-1]) {
				c[i][j] = c[i - 1][j - 1] + 1;
			}
			else {
				c[i][j] = max(c[i][j - 1], c[i - 1][j]);
			}
		}
	}
		
}

int main()
{
	{
		for (int x = 0; x < 8; x++)
		{
			for (int y = 0; y < 7; y++)
			{
				c[x][y] = -1;
			}
		}
		char A[7] = { 'A','B','C','B','D','A','B' }; //7
		char B[6] = { 'B','D','C','A','B','A' }; //6
		LCS(A, B, 7, 6);
		for (int x = 0; x < 8; x++)
		{
			for (int y = 0; y < 7; y++)
			{
				cout << c[x][y]<<
					" ";
			}
			cout <<"\n";
		}
	}
	{
		for (int x = 0; x < 8; x++)
		{
			for (int y = 0; y < 7; y++)
			{
				c[x][y] =0;
			}
		}

		

		
		char A[7] = { 'A','B','C','B','D','A','B' };
		char B[6] = { 'B','D','C','A','B','A' };
		LCS1(A, B, 7, 6);

		cout << "\n";
		for (int x = 0; x <= 7; x++)
		{
			for (int y = 0; y <= 6; y++)
			{
				cout << c[x][y] << " ";
			}
			cout << "\n";
		}
	}
	int a;
	cin >>a;
	return 0;
}

在这里插入图片描述

发布了124 篇原创文章 · 获赞 92 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_42146775/article/details/103301179