5.动态规划-最长公共子序列(LCS)

问题描述

Please implement solution for Longest Common Subsequence problem with any programming language and analyze its time complexity.

翻译

请使用任何编程语言实现最长公共子序问题的解决方案,并分析其时间复杂性。

最长公共子序列:找两个数组中的最大子集

思路

  1. 初始化 a[i][0] = 0 和 a[0][j] = 0,表示当其中一个字符串为空时,最长公共子序列的长度为 0。
  2. 从 i=1 和 j=1 开始遍历两个字符串,如果 x[i] == y[j],则 a[i][j] = a[i-1][j-1] + 1,表示当前字符属于最长公共子序列,且长度加一;否则,取 a[i-1][j] 和 a[i][j-1] 中的较大值。
  3. 在填表的同时,使用另一个二维数组 flag 记录每个子问题的解的来源,方便后续的回溯。
  4. 最后,从 flag 数组中根据填表的结果进行回溯,找出最长公共子序列。

        

代码:第一二种利于理解回溯

1.严格填表

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <algorithm>
#include <stdlib.h>
#include <vector>
using namespace std;
//The longest commom subsequence

const int N=1e3+10;
int LCSlength(char*x,char *y ,vector<vector<int> >& a, vector<vector<int> >& flag, int n, int m)
{
	for (int i = 0; i <m; i++)//在开始时,只有X或只有Y无公共序列
		a[i][0] = 0;
	for (int i = 0; i <n; i++)
		a[0][i] = 0;
	for (int i = 1; i <=n; i++)
	{
		for (int j = 1; j <=m; j++)
		{
			if (x[i] == y[j])//相同,加入子序列
			{
				a[i][j] = a[i - 1][j - 1] + 1;
				flag[i][j] = 1;//flag=1表示相同
			}
			else if (a[i - 1][j] >=a[i][j - 1])
			{
				a[i][j] = a[i - 1][j];
				flag[i][j] = 2;//flag=2表示Xi-1和Yj得来
			}
			else
			{
				a[i][j] = a[i][j - 1];
				flag[i][j] = 3;//flag=3表示由Xi和Yj-1得来
			}
		}
	}
	return a[n][m];
}
void LCS(char *a,  vector<vector<int> >& flag, int i, int j)//x和y任意一个可以做参数传过来,i,j表示当前递归到的位置
{
	if (i == 0 || j == 0)
		return;
	if (flag[i][j] == 1)
	{
		LCS(a, flag, i - 1, j - 1);//试想i,j相同,不应该i-1 与j 或j-1 与i比较(图)
		cout << a[i]<<" ";//最后一个相同子序列,所以应该先递归在输出 //为什么不输出a[j]?
	}
	else if (flag[i][j] == 2)
	{
		LCS(a, flag, i - 1, j);//从a[i-1][j]得来,说明有[i-1][j]	
		//为什么此处不输出?//由他得来并不是公共子序列
	}
	else
	{
		LCS(a, flag, i , j-1);//同上
	}
}
int main()
{
	char x[N], y[N];
	int n=0, m = 0;
	while (1)
	{
		char ch = getchar();
		if (ch == '\n')
			break;
		x[n++] = ch;
	}
	while (1)
	{
		char ch = getchar();
		if (ch == '\n')
			break;
		y[m++] = ch;
	}
	vector<vector<int> >a(n+1,vector<int>(m+1,0));//a[i][j]代表遍历到第Xi与Yj的最长公共子序列的长度
	vector<vector<int>>flag(n+1, vector<int>(m+1,0));//代表第a[i][j]个的由来,方便回溯
	int length = LCSlength(x, y, a, flag, n, m);
	LCS(x, flag, n-1, m-1);
	return 0;
}

2.中规中矩

//时间复杂度是O(a*b),最长公共子序列问题
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
string Lcs(string X, string Y)
{
	int a = X.length();
	int b = Y.length();
	vector<vector<int>> dp(a + 1, vector<int>(b + 1));
	for (int i = 1; i <= a; i++)
	{
		for (int j = 1; j <= b; j++)
		{
			if (X[i - 1] == Y[j - 1])
			{
				dp[i][j] = dp[i - 1][j - 1] + 1;
			}
			else
			{
				dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
			}
		}
	}
	string lcs;
	int i = a;
	int j = b;
	while (i > 0 && j > 0) {
		if (X[i - 1] == Y[j - 1]) {
			lcs.push_back(X[i - 1]);
			--i;
			--j;
		}
		else if (dp[i - 1][j] > dp[i][j - 1]) {//由前一个状态得来
			--i;
		}
		else {
			--j;
		}
	}
	reverse(lcs.begin(), lcs.end());
	return lcs;
}
int main()

{
	string X = "ABCBDAB";
	string Y = "BDCABA";
	cout << "The LCS of " << X << " and " << Y << " is " << Lcs(X, Y);
	return 0;
}

3.投机该死

#include<bits/stdc++.h>
using namespace std;
int main()
{
	string s1, s2;
	cin >> s1 >> s2;
	int n = s1.length(), m = s2.length();
	s1 = " " + s1, s2 = " " + s2;
	vector<vector<int>>dp(n+1,vector<int>(m+1));
	vector<char>res;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			if (s1[i] == s2[j]) { res.push_back(s1[i]); dp[i][j] = dp[i - 1][j - 1] + 1; }
			else { dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); }
		}
	}
	for (auto x : res) { cout << x; }
	cout <<endl<< dp[n][m];
	return 0;
}

try

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <algorithm>
#include <stdlib.h>
#include <vector>
using namespace std;





string LSC(string a,string b)
{
	string s;
	vector<vector<int>>dp(a.size() + 1, vector<int>(b.size() + 1,0));//多一行多一列,初始化为0
	for(int i=1;i<=a.size();i++)
	{
		for (int j = 1; j <= b.size() ; j++)
		{
			if (a[i - 1] == b[j - 1])//第一种情况 相等则+1
			{
				dp[i][j] = dp[i - 1][j - 1] + 1;
			}
			else
				dp[i][j] = max(dp[i - 1][j], dp[i][j-1]);//一个前面-1 一个后面-1,对称【cy】
		}
	}
	int i = a.size(), j = b.size();
	while (i > 0 && j > 0)
	{
		if (a[i - 1] == b[j - 1])
		{
			s.push_back(a[i - 1]);//【cy】 pushback
			i--; j--;//【cy】这里也要减
		}
		else if (dp[i - 1][j] > dp[i][j - 1])//【cy对称】
			i--;
		else
			j--;
	}
	reverse(s.begin(), s.end());//【cy】
	return s;
}

int main()
{
	string a, b;
	cin >> a >> b;
	string ans = LSC(a, b);
	cout << ans << endl;
	return 0;
}

测试案例

BDCABA
ABCBDAB

运行结果

BCBA

时间复杂度分析

O(m*n)

速记

vector<vector<int> >a(n+1,vector<int>(m+1,0));//a[i][j]代表遍历到第Xi与Yj的最长公共子序列的长度
vector<vector<int>>flag(n+1, vector<int>(m+1,0));//代表第a[i][j]个的由来,方便回溯

其中n+1、m+1是表格中多出一行一列0 

注意看几个【cy】之处,当a[i-1]!=b[j-1]时候,应该在dp[i-1][j]、dp[j-1][i]里面取最大值

回溯时从后往前回溯,不管怎么样都有东西要--,哪个大哪个--。

要reverse

两个for是<=size(),不是size+1

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熟人看不到

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值