当前位置:   article > 正文

算法:动态规划——最长公共子序列_最长公共子序列动态规划算法

最长公共子序列动态规划算法

一、动态规划概念

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。

与分治法不同的是,适合于用动态规划法求解的问题,经分解得到的子问题往往不是互相独立的。若用分治法解这类问题,则分解得到的子问题数目太多,以至于最后解决原问题需要耗费指数时间。

然而,不同子问题的数目常常只有多项式量级。在用分治法求解时,有些子问题被重复计算了许多次。如果能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,就可以避免大量重复计算,从而得到多项式时间算法。为了达到这个目的,可以用一个表来记录所有已解决的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填人表中。这就是动态规划法的基本思想。具体的动态规划算法是多种多样的,但它们具有相同的填表格式。

1. 动态规划步骤

(1) 找出最优解的性质,并刻画其结构特征;
(2)递归地定义最优值;
(3)以自底向上的方式计算出最优值;
(4)根据计算最优值时得到的信息,构造最优解。

最长公共子序列问题

题目

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
  • 1

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例

示例1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3
  • 1
  • 2
  • 3

示例2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3
  • 1
  • 2
  • 3

示例3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0
  • 1
  • 2
  • 3

分析

设两个序列为
X = { x1, x2, x3, …, xm - 1, xm} ,元素个数为m个。
Y = { y1, y2, y3, …, yn - 1, yn},元素个数为n个。

设公共子序列为:
Z = { z1, z2, z3, …, zk - 1, zk }, 元素个数为k个。

情况分析:

  1. 如果 xm == yn,则 zk = xm = yn,最后一个元素相等,那也就意味着我们可以把问题规模缩小,使得:
zk - 1 = xm - 1 && yn - 1
  • 1

原规模的最长公共子序列长度就是 Z(k - 1) + 1

  1. 如果 xm != yn, xm != zk;那么
zk = xm - 1 && yn
  • 1
  1. 如果yn != xm, yn != zk, 那么
zk = xm && yn - 1
  • 1

令 c [m][n]为最长公共子序列,m为序列X的元素个数,n为Y序列的元素个数, 那么会得到以下情况:

  1. 当m或n为0时,最长公共子序列为0。
c[m][n] = (m == 0 || n == 0) ? 0;
  • 1
  1. 当m > 1, n > 1, xm == yn时
c[m][n] = (xm == yn) ? c[m - 1][n - 1] + 1;
  • 1
  1. 当 m > 1, n > 1, xm != yn时
c[m][n] = (xm != yn) ? max(c[m - 1][n], c[m][n - 1]);
  • 1

在这里插入图片描述

代码(递归)

int LCSLength(char* X, char* Y, int m, int n)
{
	if (m == 0 || n == 0) return 0;
	else
	{
		if (X[m] == Y[n])
			return LCSLength(X, Y, m - 1, n - 1) + 1;
		else
		{
			int max1 = LCSLength(X, Y, m - 1, n);
			int max2 = LCSLength(X, Y, m, n - 1);
			return max1 > max2 ? max1 : max2;
		}
	}
}

int main(void)
{
	char X[] = { "ABCBDAB" };
	char Y[] = { "BDCABA" };
	int xm = strlen(X), yn = strlen(Y);

	int maxlen = LCSLength(X, Y, xm, yn);
	cout << maxlen << endl;

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

运行结果:
在这里插入图片描述

查表

#include<iomanip>

template<typename T>
void Print_vec(vector<vector<T> >& c)
{
	int m = c.size();
	for (int i = 0; i < m; ++i)
	{
		int n = c[i].size();
		for (int j = 0; j < n; ++j)
		{
			cout << setw(3) << c[i][j];
		}
		cout << endl;
	}
	cout << endl;
}

int LCSLength(char* X, char* Y, int m, int n, 
	vector<vector<int> >& c, vector<vector<int> >& s)
{
	if (m == 0 || n == 0) return 0;
	else if (c[m][n] != 0) return c[m][n];
	else
	{ 
		if (X[m] == Y[n])
		{
			c[m][n] = LCSLength(X, Y, m - 1, n - 1, c, s) + 1;
			s[m][n] = 1;
		}
		else
		{
			int max1 = LCSLength(X, Y, m - 1, n, c, s);
			int max2 = LCSLength(X, Y, m, n - 1, c, s);
			if (max1 > max2)
			{
				c[m][n] = max1;
				s[m][n] = 2;
			}
			else
			{
				c[m][n] = max2;
				s[m][n] = 3;
			}
		}
	}
	return c[m][n];
}

int main(void)
{
	char X[] = { "#ABCBDAB" };
	char Y[] = { "#BDCABA" };
	int xm = strlen(X) - 1, yn = strlen(Y) - 1;

	vector<vector<int> > c, s;
	c.resize(xm + 1);
	s.resize(xm + 1);
	for (int i = 0; i < xm + 1; ++i)
	{
		c[i].resize(yn + 1);
		s[i].resize(yn + 1);
	}

	int maxlen = LCSLength(X, Y, xm, yn, c, s);

	Print_vec(c);
	Print_vec(s);
	cout << maxlen << endl;

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

在这里插入图片描述
在这里插入图片描述

这样可得出最长公共子序列是哪些字符。

打印最长公共子序列

void LCS(char* X, vector<vector<int> >& s, int i, int j)
{
	if (i == 0 || j == 0) return;
	if (s[i][j] == 1)
	{
		LCS(X, s, i - 1, j - 1);
		cout << X[i];
	}
	else if (s[i][j] == 2)
	{
		LCS(X, s, i - 1, j);
	}
	else
	{
		LCS(X, s, i, j - 1);
	}
}

int main(void)
{
	char X[] = { "#ABCBDAB" };
	char Y[] = { "#BDCABA" };
	int xm = strlen(X) - 1, yn = strlen(Y) - 1;

	vector<vector<int> > c, s;
	c.resize(xm + 1);
	s.resize(xm + 1);
	for (int i = 0; i < xm + 1; ++i)
	{
		c[i].resize(yn + 1);
		s[i].resize(yn + 1);
	}

	int maxlen = LCSLength(X, Y, xm, yn, c, s);

	Print_vec(c);
	Print_vec(s);
	cout << maxlen << endl;

	LCS(X, s, xm, yn);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

运行结果:

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/169542
推荐阅读
相关标签
  

闽ICP备14008679号