力扣1141: 求最长公共子序列

Description

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

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

例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

Analysis

注意题目要求的是公共子序列,与字串需要加以区分:
子串必须是连续的,而子序列不一定连续。
如:字符串 " abd "" abcd " 的最长公共字符串为 " ab ", 最长公共子序列为 “abd”
弄清楚了什么是子序列,我们开始分析:
还是上边的例子:求字符串 str1 = " abd ""str2 = abcd " 的最长子序列。
我们从后往前注意比较两个字符串:
str1[2] == str2[3],两字符相等,那么最终的答案中一定包含该字符,继续往前比较, str1[1] != str2[2],我们需要再次寻找相同的字符,这时可以有两种方式:str1指针往前移 or str2指针前移。求最值问题可以使用动态规划方法,根据上边的分析:

  • dp[i][j]表示str1长度为i、str2长度为j时最长公共序列长度,因而需要创建一个str1长度加1 行、str2长度加1 列的二维数组(保证了下标可以到达 str1.length() 以及 str2.length())
  • 根据dp数组的含义:当str1[i] == str2[j] 时,dp[i][j] =dp[i - 1][j - 1] + 1; str1[i] != str2[j] 时, dp[i][j] = max(dp[i - 1][j], dp[i][j -1]);
    在这里插入图片描述
  • 由递推公式,遍历顺序为从左至右(列方向),从上往下(行方向);考虑到dp[i][j]可能会使用dp[i - 1][j] 以及 dp[i][j - 1],因此对于边界需要特殊处理,一种行之有效的方法是对dp数组行、列都扩充一行。在创建dp数组的时候已经保证了这一点。
    对于字符串 " ABCBDAB "" BDCABA ", 最后填充完毕的dp数组为
    在这里插入图片描述
    箭头表示该处的结果建立在之前的某结果上。(方便查看)

Code

#include <iostream>
#include <string>
#include <vector>
using namespace std;
void subSequence(string& str1, string& str2, vector<vector<int>>& dp);
void backTrace(string& str1, string& str2, vector<vector<int>>& dp);

//求最长子序列长度
void subSequence(string& str1, string& str2,vector<vector<int>>& dp) {
	for (int row = 1; row <= str1.length(); row++) {
		for (int col = 1; col <= str2.length(); col++) {
			if (str1[row - 1] == str2[col - 1])
				dp[row][col] = dp[row - 1][col - 1] + 1;
			else
				dp[row][col] = max(dp[row - 1][col], dp[row][col - 1]);
		}
	}
	backTrace(str1, str2, dp);
}
//回溯查找出最长子序列
void backTrace(string& str1, string& str2, vector<vector<int>>& dp) {
	string res = "";
	int curRow = dp.size() - 1, curCol = dp[0].size() - 1;
	while (curRow > 0 && curCol > 0) {
		if (str1[curRow - 1] == str2[curCol - 1]) {
			res.insert(res.begin(), str1[curRow - 1]);
			curRow--, curCol--;
		}
		else {
			if (dp[curRow - 1][curCol] >= dp[curRow][curCol - 1]) {
				curRow--;
			}
			else curCol--;
		}
	}
	cout << "最长公共子序列为: "<< res;
}

int main() {
	string str1, str2;
	cin >> str1 >> str2;
	//n + 1行 m + 1列
	//dp[i][j] 表示str1长度为 i、str2长度为j 时的最长公共子序列长度
	vector<vector<int>> dp(str1.length() + 1, vector<int>(str2.length() + 1, 0));
	subSequence(str1, str2, dp);
	return 0;
}

Running

在这里插入图片描述

相似题

在阅读完本文之后,可以尝试以下类似题:
巧妙利用LCS求解 ---->>>>> 583. 两个字符串的删除操作
LCS系列经典面试题 ---->>>>> 1035. 不相交的线

变体 ----->>>>> 最长重复子数组

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

nepu_bin

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

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

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

打赏作者

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

抵扣说明:

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

余额充值