最长公共子序列(经典dp题)

题目可以看力扣上的这个:https://leetcode.cn/problems/qJnOS7/

dp求最长子序列的问题,过长的字符串处理不了,因为dp过程中要用到二维数组,二维数组定义不能过大。

下面代码我做了扩展,增加了输出最长子序列字符串的部分。总得来说目前看到的输出所求字符串的做法有两种:
一:遍历过程中直接用string的二维数组记录。
二:回溯法:先遍历求出长度,过程中记录获得数值的路径。后续再遍历路径数组求出最长子序列。

法一代码

//求最长公共子序列,遍历过程中记录公共字符串
#include<iostream>
#include<string>
using namespace std;
string STR[1005][1005];

int main()
{
	string str1, str2;
	cin >> str1 >> str2;
	for (int i = 0; i < str1.size(); i++)
	{
		for (int j = 0; j < str2.size(); j++)
		{
			if (str1[i] == str2[j])
			{
				STR[i + 1][j + 1] = STR[i][j] + str2[j];//str数组下标从0开始,为避免0-1越界情况,向后延伸(不用STR[i][j]=STR[i-1][j-1]+1)
			}
			else
			{
				if (STR[i][j + 1].size() > STR[i + 1][j].size())
					STR[i + 1][j + 1] = STR[i][j + 1];
				else
					STR[i + 1][j + 1] = STR[i + 1][j];
			}
		}
	}
	cout << STR[str1.size()][str2.size()].size() << '\n';
	cout << STR[str1.size()][str2.size()]<< '\n';
	return 0;
}

法二代码

对于路径数组c,规定
c=1记录数值从对角线方向传来,且值改变加1了(即d[i + 1][j + 1] = d[i][j] + 1)
c=2记录数值从左传来(d[i + 1][j + 1] = d[i][j + 1])
c=3记录数值从上传来(d[i + 1][j + 1] = d[i + 1][j])
且当左,上两个方向数值相等时,规定数值由从上传来得到

//求最长公共子序列,先确定最大长度,再回溯得到所求字符串
#include<iostream>
#include<string>
#include<algorithm>
#include<stack>
using namespace std;
int d[10005][10005];
int c[10005][10005];
stack<char> s;
int main()
{
	string str1, str2;
	cin >> str1 >> str2;
	for (int i = 0; i < str1.size(); i++)
		for (int j = 0; j < str2.size(); j++)//str数组下标从0开始,但d数组需要从前方传递,在0处-1会越界,因此后延(从1行1列开始记录数据)
		{
			if (str1[i] == str2[j])
			{
				d[i + 1][j + 1] = d[i][j] + 1;
				c[i + 1][j + 1] = 1;
			}
			else
			{
				if (d[i + 1][j] >= d[i][j + 1])//当左,上两个方向数值相等时,规定数值由从上传来得到
				{
					d[i + 1][j + 1] = d[i + 1][j];
					c[i + 1][j + 1] = 3;
				}
				else
				{
					d[i + 1][j + 1] = d[i][j + 1];
					c[i + 1][j + 1] = 2;
				}
			}
		}
	cout << d[str1.size()][str2.size()] << '\n';
	int i = str1.size(), j = str2.size();
	while (i > 0 && j > 0)//回溯路径数组c,找出最长子序列组成部分
	{
		if (c[i][j] == 1)//根据栈先进后出的特点,取它记录记录路径,后续输出即可得到正序路径
		{
			s.push(str1[i - 1]);//因为前面遍历记录路径时,记录数据后延从1开始,和字符串的对应要对应-1
			i--;//例如:0行0列在d,c数组中是初始化未记录数据的地方,但str1[0]='a',str2[0]='a'
			j--;
		}
		else if (c[i][j] == 2)
			i--;
		else
			j--;
	}
	while (s.size())
	{
		cout << s.top();
		s.pop();
	}
	cout << '\n';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值