【算法与数据结构】718、1143、1035、392、115、LeetCode最长重复子数组+最长公共子序列+不相交的线+判断子序列+不同的子序列

文章目录

  • 一、718、最长重复子数组
  • 二、1143、最长公共子序列
  • 三、1035、不相交的线
  • 四、392、判断子序列
  • 五、115、不同的子序列
  • 六、完整代码

所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。

一、718、最长重复子数组

在这里插入图片描述

  思路分析

  • 第一步,动态数组的含义。 d p [ i ] [ j ] dp[i][j] dp[i][j]代表以下标 i − 1 i - 1 i1为结尾的nums1,和以下标 j − 1 j - 1 j1为结尾的nums2,最长重复子数组长度为 d p [ i ] [ j ] dp[i][j] dp[i][j]
  • 第二步,递推公式。根据 d p [ i ] [ j ] dp[i][j] dp[i][j]的定义, d p [ i ] [ j ] dp[i][j] dp[i][j]的状态只能由 d p [ i − 1 ] [ j − 1 ] dp[i - 1][j - 1] dp[i1][j1]推导出来。
	if (nums1[i - 1] == nums2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
  • 第三步,元素初始化。dp数组中的所有元素都初始化为0。
  • 第四步,递归顺序。一共有两层循环,先遍历nums1或者先遍历nums2都可以。
  • 第五步,打印结果。题目要求长度最长的子数组的长度。所以在遍历的时候顺便把 d p [ i ] [ j ] dp[i][j] dp[i][j]的最大值记录下来。
      程序如下
// 718、最长重复子数组
class Solution {
public:int findLength(vector<int>& nums1, vector<int>& nums2) {vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 0));int result = 0;for (int i = 1; i <= nums1.size(); i++) {for (int j = 1; j <= nums2.size(); j++) {if (nums1[i - 1] == nums2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;if (dp[i][j] > result) result = dp[i][j];}}return result;}
};

复杂度分析:

  • 时间复杂度: O ( n ∗ m ) O(n*m) O(nm) n n n m m m分别是两个数组的长度。
  • 空间复杂度: O ( n ∗ m ) O(n*m) O(nm)

二、1143、最长公共子序列

在这里插入图片描述

  思路分析

  1. 第一步,动态数组的含义。 d p [ i ] [ j ] dp[i][j] dp[i][j]代表以下标 i − 1 i - 1 i1为结尾的text1,和以下标 j − 1 j - 1 j1为结尾的text2,最长公共子序列长度为 d p [ i ] [ j ] dp[i][j] dp[i][j]
  2. 第二步,递推公式。 d p [ i ] [ j ] dp[i][j] dp[i][j]可以由两种情况推导出来:
  • t e x t 1 [ i − 1 ] text1[i - 1] text1[i1] t e x t 2 [ j − 1 ] text2[j - 1] text2[j1]相同:那么找到一个公共元素, d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j] = dp[i - 1][j - 1] + 1 dp[i][j]=dp[i1][j1]+1
  • t e x t 1 [ i − 1 ] text1[i - 1] text1[i1] t e x t 2 [ j − 1 ] text2[j - 1] text2[j1]不相同:那么 t e x t 1 [ 0 , i − 2 ] text1[0, i - 2] text1[0,i2] t e x t 2 [ 0 , j − 1 ] text2[0, j - 1] text2[0,j1]的最长公共子序列和 t e x t 1 [ 0 , i − 1 ] text1[0, i - 1] text1[0,i1] t e x t 2 [ 0 , j − 2 ] text2[0, j - 2] text2[0,j2]的最长公共子序列,取最大的。
	if (text1[i - 1] == text2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
  1. 第三步,元素初始化。dp数组中的所有元素都初始化为0。
  2. 第四步,递归顺序。一共有两层循环,从前往后进行遍历。
  3. 第五步,打印结果。题目要求最长公共子序列的长度。所以在遍历的时候顺便把 d p [ i ] [ j ] dp[i][j] dp[i][j]的最大值记录下来。
      程序如下
// 1143、最长公共子序列
class Solution2 {
public:int longestCommonSubsequence(string text1, string text2) {vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));int result = 0;for (int i = 1; i <= text1.size(); i++) {for (int j = 1; j <= text2.size(); j++) {if (text1[i - 1] == text2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);if(dp[i][j] > result) result = dp[i][j];}}return result;}
};

复杂度分析:

  • 时间复杂度: O ( n ∗ m ) O(n*m) O(nm) n n n m m m分别是两个序列的长度。
  • 空间复杂度: O ( n ∗ m ) O(n*m) O(nm)

三、1035、不相交的线

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

  思路分析:本题要求绘制的最大连线数,实际上就是求两个字符串的最长公共子序列的长度,即1143、最长公共子序列这道题。我们将字符串改成数组,代码完全一样,直接copy过来。
  程序如下

// 1035、不相交的线
class Solution3 {
public:int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 0));int result = 0;for (int i = 1; i <= nums1.size(); i++) {for (int j = 1; j <= nums2.size(); j++) {if (nums1[i - 1] == nums2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);if (dp[i][j] > result) result = dp[i][j];}}return result;}
};

复杂度分析:

  • 时间复杂度: O ( n ∗ m ) O(n*m) O(nm) n n n m m m分别是两个数组的长度。
  • 空间复杂度: O ( n ∗ m ) O(n*m) O(nm)

四、392、判断子序列

在这里插入图片描述

  思路分析:本题的思路和1143、最长公共子序列的分析思路差不多,主要区别在于本题判断的是“ 最长公共子序列是不是另一个字符串的子串”。那么我们找到二者的最长公共子串,判断其长度是否等于s的长度即可。

  1. 第一步,动态数组的含义。 d p [ i ] [ j ] dp[i][j] dp[i][j]代表以下标 i − 1 i - 1 i1为结尾的s,和以下标 j − 1 j - 1 j1为结尾的t,最长公共子序列长度为 d p [ i ] [ j ] dp[i][j] dp[i][j]
  2. 第二步,递推公式。 d p [ i ] [ j ] dp[i][j] dp[i][j]可以由两种情况推导出来:
  • s [ i − 1 ] s[i - 1] s[i1] t [ j − 1 ] t[j - 1] t[j1]相同:那么找到一个公共元素, d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j] = dp[i - 1][j - 1] + 1 dp[i][j]=dp[i1][j1]+1
  • s [ i − 1 ] s[i - 1] s[i1] t [ j − 1 ] t[j - 1] t[j1]不相同:那么 d p [ i ] [ j ] dp[i][j] dp[i][j]等于 s [ 0 , i − 1 ] s[0, i - 1] s[0,i1] t [ 0 , j − 2 ] t[0, j - 2] t[0,j2]的最长公共子序列。
	if (s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = dp[i][j - 1];		// 与1143不同的地方
  1. 第三步,元素初始化。dp数组中的所有元素都初始化为0。
  2. 第四步,递归顺序。一共有两层循环,从前往后进行遍历。
  3. 第五步,打印结果。题目要求最长公共子序列的长度。所以在遍历的时候顺便把 d p [ i ] [ j ] dp[i][j] dp[i][j]的最大值记录下来,在用三目运算符返回。
	return result == s.size() ? true : false;	// 与1143不同的地方

  程序如下

// 392、判断子序列-动态规划
class Solution4 {
public:bool isSubsequence(string s, string t) {vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));int result = 0;for (int i = 1; i <= s.size(); i++) {for (int j = 1; j <= t.size(); j++) {if (s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = dp[i][j - 1];		// 与1143不同的地方if (dp[i][j] > result) result = dp[i][j];}}return result == s.size() ? true : false;	// 与1143不同的地方}
};

复杂度分析:

  • 时间复杂度: O ( n ∗ m ) O(n*m) O(nm) n n n m m m分别是两个字符串的长度。
  • 空间复杂度: O ( n ∗ m ) O(n*m) O(nm)

五、115、不同的子序列

在这里插入图片描述

  思路分析:本题的思路和1143、最长公共子序列的分析思路差不多。本题统计字符串t在字符串s中出现的次数,我们可以理解为删除掉字符串s中的部分字符使得字符串s和字符串t相同的方法数量。

  1. 第一步,动态数组的含义。 d p [ i ] [ j ] dp[i][j] dp[i][j]代表以下标 j − 1 j - 1 j1为结尾的t在以下标 i − 1 i - 1 i1为结尾的s中出现的次数为 d p [ i ] [ j ] dp[i][j] dp[i][j],即 t [ 0 , j − 1 ] t[0, j-1] t[0,j1] s [ 0 , i − 1 ] s[0, i-1] s[0,i1]中出现的次数。

  2. 第二步,递推公式。 d p [ i ] [ j ] dp[i][j] dp[i][j]可以由两种情况推导出来:

  • s [ i − 1 ] s[i - 1] s[i1] t [ j − 1 ] t[j - 1] t[j1]相同:此时的 d p [ i ] [ j ] dp[i][j] dp[i][j]由两部分组成。一部分是用 s [ i − 1 ] s[i-1] s[i1]来匹配:相当于在 s [ 0 , i − 2 ] s[0, i-2] s[0,i2]中寻找 t [ 0 , j − 2 ] t[0, j-2] t[0,j2]的个数(剩下一个字符 s [ i − 1 ] s[i - 1] s[i1] t [ j − 1 ] t[j - 1] t[j1]已经匹配了),即 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1];另一部分是不用 s [ i − 1 ] s[i-1] s[i1]来匹配,相当于在 s [ 0 , i − 2 ] s[0, i-2] s[0,i2]中寻找 t [ 0 , j − 1 ] t[0, j-1] t[0,j1]的个数,即 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]
  • s [ i − 1 ] s[i - 1] s[i1] t [ j − 1 ] t[j - 1] t[j1]不相同:那么 s [ 0 , i − 2 ] s[0, i - 2] s[0,i2]中, t [ 0 , j − 1 ] t[0, j - 1] t[0,j1]的数量和 s [ 0 , i − 1 ] s[0, i - 1] s[0,i1]中, t [ 0 , j − 1 ] t[0, j - 1] t[0,j1]的数量相同。 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i1][j]
	if (s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];	else dp[i][j] = dp[i - 1][j];

  例子:s=“bageg”,t=“bag”。那么用s[4]="g"组成bag的方法数量,相当于在s[0, 3]="bage"中寻找中t[0, 1]="ba"的个数,只有s[0]s[1]s[4]这一种。而不用s[4]="g"组成bag的方法数量,相当于在s[0,3] ="bage"中,寻找t[0,2]="bag"的个数,即dp[4, 3],只有s[0]s[1]s[2]这一种。(说明:dp[4,2]=1代表在s[0,3] ="bage"中,t[0,1]="ba"的个数为1。)

在这里插入图片描述

  1. 第三步,元素初始化。 d p [ i ] [ 0 ] dp[i][0] dp[i][0](第一列)表示字符串 s [ 0 , i − 1 ] s[0, i-1] s[0,i1]中可以随便删除元素,出现空字符串的个数。 d p [ 0 ] [ j ] dp[0][j] dp[0][j](第一行)表示空字符串 s s s,出现字符串 t [ 0 , j − 1 ] t[0, j-1] t[0,j1]的个数。其中,空字符串s中空字符串t的个数为1。那么 d p [ 0 ] [ 0 ] = 1 , d p [ i ] [ 0 ] = 1 , d p [ 0 ] [ j ] = 0 dp[0][0]=1, dp[i][0] = 1, dp[0][j] = 0 dp[0][0]=1,dp[i][0]=1,dp[0][j]=0
  2. 第四步,递归顺序。一共有两层循环,从前往后进行遍历。
  3. 第五步,打印结果。
      程序如下
// 115、不同的子序列-动态规划
class Solution5 {
public:int numDistinct(string s, string t) {vector<vector<uint64_t>> dp(s.size() + 1, vector<uint64_t>(t.size() + 1, 0));for (int i = 0; i <= s.size(); i++) dp[i][0] = 1;		// 第一列初始化为1, dp[0][0]为1for (int j = 1; j <= t.size(); j++) dp[0][j] = 0;		// 第一行初始化为0, 可以省略for (int i = 1; i <= s.size(); i++) {for (int j = 1; j <= t.size(); j++) {if (s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];	else dp[i][j] = dp[i - 1][j];}}return dp[s.size()][t.size()];}
};

复杂度分析:

  • 时间复杂度: O ( n ∗ m ) O(n*m) O(nm) n n n m m m分别是两个字符串的长度。
  • 空间复杂度: O ( n ∗ m ) O(n*m) O(nm)

六、完整代码

# include <iostream>
# include <vector>
# include <string>
using namespace std;// 718、最长重复子数组
class Solution {
public:int findLength(vector<int>& nums1, vector<int>& nums2) {vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 0));int result = 0;for (int i = 1; i <= nums1.size(); i++) {for (int j = 1; j <= nums2.size(); j++) {if (nums1[i - 1] == nums2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;if (dp[i][j] > result) result = dp[i][j];}}return result;}
};// 1143、最长公共子序列
class Solution2 {
public:int longestCommonSubsequence(string text1, string text2) {vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));int result = 0;for (int i = 1; i <= text1.size(); i++) {for (int j = 1; j <= text2.size(); j++) {if (text1[i - 1] == text2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);if (dp[i][j] > result) result = dp[i][j];}}return result;}
};// 1035、不相交的线-动态规划
class Solution3 {
public:int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 0));int result = 0;for (int i = 1; i <= nums1.size(); i++) {for (int j = 1; j <= nums2.size(); j++) {if (nums1[i - 1] == nums2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);if (dp[i][j] > result) result = dp[i][j];}}return result;}
};// 392、判断子序列-动态规划
class Solution4 {
public:bool isSubsequence(string s, string t) {vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));int result = 0;for (int i = 1; i <= s.size(); i++) {for (int j = 1; j <= t.size(); j++) {if (s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = dp[i][j - 1];		// 与1143不同的地方if (dp[i][j] > result) result = dp[i][j];}}return result == s.size() ? true : false;	// 与1143不同的地方}
};// 115、不同的子序列-动态规划
class Solution5 {
public:int numDistinct(string s, string t) {vector<vector<uint64_t>> dp(s.size() + 1, vector<uint64_t>(t.size() + 1, 0));for (int i = 0; i <= s.size(); i++) dp[i][0] = 1;		// 第一列初始化为1, dp[0][0]为1for (int j = 1; j <= t.size(); j++) dp[0][j] = 0;		// 第一行初始化为0, 可以省略for (int i = 1; i <= s.size(); i++) {for (int j = 1; j <= t.size(); j++) {if (s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];	else dp[i][j] = dp[i - 1][j];}}return dp[s.size()][t.size()];}
};int main() {//vector<int> nums1 = { 1, 2, 3, 2, 1 }, nums2 = { 3, 2, 1, 4, 7 };		// 测试案例//Solution s1;//int result = s1.findLength(nums1, nums2);//string text1 = "abcde", text2 = "ace";		// 测试案例//Solution2 s1;//int result = s1.longestCommonSubsequence(text1, text2);//vector<int> nums1 = { 1, 4, 2 }, nums2 = { 1, 2, 4 };		// 测试案例//Solution3 s1;//int result = s1.maxUncrossedLines(nums1, nums2);//string s = "abc", t = "ahbgdc";			// 测试案例//Solution4 s1;//int result = s1.isSubsequence(s, t);string s = "babgbag", t = "bag";		// 测试案例Solution5 s1;int result = s1.numDistinct(s, t);cout << result << endl;system("pause");return 0;
}

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/454440.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

88 docker 环境下面 前端A连到后端B + 前端B连到后端A

前言 呵呵 最近出现了这样的一个问题, 我们有多个前端服务, 分别连接了对应的后端服务, 前端A -> 后端A, 前端B -> 后端B 但是 最近的时候 却会出现一种情况就是, 有些时候 前端A 连接到了 后端B, 前端B 连接到了 后端A 我们 前端服务使用 nginx 提供前端 html, js…

K8S之Namespace的介绍和使用

Namespace的理论和实操 Namespace理论说明Namespace实操创建、查看命名空间使用ResouceQuota 对Namespace做资源限额更多ResouceQuota 的使用 Namespace理论说明 命名空间定义 K8s支持多个虚拟集群&#xff0c;它们底层依赖于同一个物理集群。 这些虚拟集群被称为命名空间&…

一行命令找出 Linux 中所有真实用户

哈喽大家好&#xff0c;我是咸鱼。 接触过 Linux 的小伙伴们都知道在 Linux &#xff08;或者说类 Unix&#xff09;中&#xff0c;有三种类型的用户&#xff1a; 超级用户&#xff08;UID 为 0&#xff09;&#xff1a;即 root 用户&#xff0c;拥有最高权限。系统用户&…

【ACS】2区Top,又牛又水,IF连续八年8+,发文量爆满5000+,录用率78%

发表说 截图来源&#xff1a;LetPub 01 期刊概况 ACS Applied Materials & Interfaces 【出版社】American Chemical Society 【ISSN】1944-8244 【EISSN】1944-8252 【期刊详情】IF&#xff1a;9.0-10.0&#xff0c;JCR1区&#xff0c;中科院2区Top&#xff1b; 【检…

【C++】I/O多路转接详解(二)

在上一篇文章【C】I/O多路转接详解&#xff08;一&#xff09; 在出现EPOLL之后&#xff0c;随之而来的是两种事件处理模式的应运而生&#xff1a;Reator 和 Proactor,同步IO模型常用于Reactor模式&#xff0c;异步IO常用于Proactor. 目录 1. 服务器编程框架简介2. IO处理1. R…

详解spring6.0新特性汇总

spring6新特性汇总 part1 spring6.0新特性 spring6.0 2022年11月。新一代框架带jdk17&jakarta ee9 https://www.graalvm.org/ part2 AOP&事务 1.AOP:面向切面编程 通过预编译方式和运行期动态 代理实现程序功能的统一维护的一种技术。 使用场景&#xff1a; 权…

SpringBoot整合Flowable最新教程(一)Flowable介绍

一、Flowable 入门介绍 代码实现文章&#xff1a;SpringBoot整合Flowable最新教程&#xff08;二&#xff09; 官网地址&#xff1a;https://www.flowable.org/   Flowable6.3中文教程&#xff1a;中文教程地址   可以在官网下载对应的jar包在本地部署运行&#xff0c;官方…

Spring5系列学习文章分享---第六篇(框架新功能系列+整合日志+ @Nullable注解 + JUnit5整合)

目录 **Spring5** 框架新功能系列一Spring 5.0 框架自带了通用的日志封装Spring5 **框架核心容器**支持Nullable **注解****Spring5** **核心容器支持函数式风格** GenericApplicationContext**Spring5** **支持整合** JUnit5感谢阅读 开篇: 欢迎再次来到 Spring 5 学习系列&am…

【Linux】文件周边002之初步理解文件管理(打开的文件)

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.&#xff08;打开…

C++ 类与对象(下)

目录 1. 再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表 1.3 explicit关键字 2. static成员 2.1 概念 2.2 特性 3.友元 3.1友元函数 3.2 友元类 4. 内部类 5.匿名对象 6.拷贝对象时的一些编译器优化 7. 再次理解类和对象 【本节目标】 1. 再谈构造函数 2. Static成员…

动态颗粒背景,适合VUE、HTML前端显示

动态颗粒背景&#xff0c;适合做背景使用&#xff0c;VUE、HTML前端显示直接看效果 废话不多说直接上代码&#xff1b; 一、html 代码部分 <template><div id"login"><div class"container"><div class"login-form"&g…

情人节浪漫礼物指南:精选共享甜蜜时光的情人节礼物推荐

情人节&#xff0c;代表着浪漫和爱意的纪念日&#xff0c;总能激起每个人内心深处的悸动&#xff0c;促使他们渴望与爱侣共度美好时刻。为爱人精心选择一份情人节礼物&#xff0c;不仅是对他们深情的告白&#xff0c;更是将这份爱升华&#xff0c;让它成为两人爱情故事里的宝贵…