最长公共子序列(动态规划)

最长公共子串问题(动态规划)

问题描述

最长公共子序列(动态规划)_第1张图片

最长公共子序列(动态规划)_第2张图片

1.最长公共子序列的结构

最长公共子序列(动态规划)_第3张图片

解释:

(1)X和Y的末尾元素相同则,公共子串一定包含末尾元素,所以公共子串是X和Y的公共子串
(2)X和Y的末尾元素不同,Y末尾元素和最长公共子序列的末尾元素相同,说明X要遍历全部,而X末尾元素无需考虑,所以最长公共子序列为X和Y的前M-1个元素
(3)X和Y的末尾元素不同,X末尾元素和最长公共子序列的末尾元素相同,说明X要遍历全部,而Y末尾元素无需考虑,所以最长公共子序列为X和Y的前N-1个元素


2.子问题的递归结构

最长公共子序列(动态规划)_第4张图片


3.计算最优值

最长公共子序列(动态规划)_第5张图片

4.代码实现

int lcs(int m, int n, string x, string y, int** c, int** b)
{
//m为字符串x的长度	n为字符串y的长度
	//c为c[i][j]数组,表示i-1长度x,j-1长度的y的最长公共子序列的长度
	//b记录路劲,1为x,y都增长,2为x增长,3为y增长
	//初始化	  如何一个字符串长度为0,则公共子串长度为0
	int i, j;
	for (i = 0; i <= m; i++) {
		c[i][0] = 0;//y为空串
		b[i][0] = 0;
	}
	for (i = 0; i <= n; i++)
	{
		c[0][i] = 0;//x为空串
		b[0][i] = 0;
	}
	for (i = 1; i <= m; i++) {
		for (j = 1; j <= n; j++) {
			if (x[i - 1] == y[j - 1])//第一种情况,末尾相同
			{
				c[i][j] = c[i - 1][j - 1] + 1;
				b[i][j] = 1;
			}
			else if (c[i - 1][j] >= c[i][j - 1]) {//第二种情况
				c[i][j] = c[i - 1][j];
				b[i][j] = 2;
			}
			else
			{
				c[i][j] = c[i][j - 1];//第三种情况
				b[i][j] = 3;
			}
		}
	}
	for (i = 0; i <= m; i++)//输出c矩阵查看
	{
		for (j = 0; j <=n; j++) {
			cout << c[i][j] << ' ';
		}
		cout << endl;
	}
	return c[m - 1][n - 1];
}

测试样例

int main()
{
	int** c, **b;
	c = new int* [10];
	b = new int* [10];
	for (int i = 0; i < 10; i++)
	{
		c[i] = new int[10]{};
		b[i] = new int[10]{};
	}
	string x = "abcbdabe";
	string y = "bdcabae";
	cout << lcs(8, 7, x, y, c, b);

}

测试结果
最长公共子序列(动态规划)_第6张图片
这里因为还有记录路径,所以写了3种情况,如果不记录路径的话只需要2种情况相同和不同,相同的情况在前一种基础上加1,不同的情况就是x小,y小2种情况中取较大者,修改关键代码图下

for (i = 1; i < m; i++) {
		for (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-1][j],c[i][j-1]);
			}
		}
	}

求出路径

最长公共子序列(动态规划)_第7张图片
上面的b矩阵记录了数组xy不同长度下的情况。我们知道情况1是x和y字母相同c[i][j] = c[i - 1][j - 1]+1,x,y都增加,符合公共子串。2 是c[i][j] = c[i - 1][j],字符串x的长度增加,是向下。3是c[i][j] = c[i][j - 1],是y字符串增加,向右。所以我们只需要从i=1,j=1开始,按照遇到1 执行i–;j–。遇到2执行i–;遇到3执行j–;遍历完矩阵b就可以得到最终的最长公共子序列

完整代码

int lcs(int m, int n, string x, string y, int** c, int** b)
{
	//m为字符串x的长度	n为字符串y的长度
	//c为c[i][j]数组,表示i-1长度x,j-1长度的y的最长公共子序列的长度
	//b记录路劲,1为x,y都增长,2为x增长,3为y增长
	//初始化	  如何一个字符串长度为0,则公共子串长度为0
	int i, j;
	for (i = 0; i <= m; i++) {
		c[i][0] = 0;//y为空串
		b[i][0] = 0;
	}
	for (i = 0; i <= n; i++)
	{
		c[0][i] = 0;//x为空串
		b[0][i] = 0;
	}
	for (i = 1; i <= m; i++) {
		for (j = 1; j <= n; j++) {
			if (x[i - 1] == y[j - 1])//第一种情况,末尾相同
			{
				c[i][j] = c[i - 1][j - 1] + 1;
				b[i][j] = 1;
			}
			else if (c[i - 1][j] >= c[i][j - 1]) {//第二种情况
				c[i][j] = c[i - 1][j];
				b[i][j] = 2;
			}
			else
			{
				c[i][j] = c[i][j - 1];//第三种情况
				b[i][j] = 3;
			}
		}
	}
	for (i = 0; i <= m; i++)//输出c矩阵查看
	{
		for (j = 0; j <=n; j++) {
			cout << c[i][j] << ' ';
		}
		cout << endl;
	}
	cout << endl;
	for (i = 0; i <= m; i++)//输出c矩阵查看
	{
		for (j = 0; j <= n; j++) {
			cout << b[i][j] << ' ';
		}
		cout << endl;
	}
	cout << endl;
	i--; j--;
	while (i > 0 && j > 0)
	{
		if (b[i][j] == 1) { cout << x[i-1]; i--; j--; }
		else if (b[i][j] == 2) { i--; }
		else { j--; }
	}
	cout << endl;
	return c[m - 1][n - 1];
}

结果
最长公共子序列(动态规划)_第8张图片

你可能感兴趣的:(笔记,c++,动态规划求解)