代码随想录训练营day53| 1143.最长公共子序列 1035.不相交的线 53. 最大子序和...

简介: 代码随想录训练营day53| 1143.最长公共子序列 1035.不相交的线 53. 最大子序和...

前言

代码随想录算法训练营day53


一、Leetcode 1143.最长公共子序列

1.题目

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

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

例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 输出:3

解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

输入:text1 = "abc", text2 = "abc" 输出:3 解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

输入:text1 = "abc", text2 = "def" 输出:0 解释:两个字符串没有公共子序列,返回 0 。

提示:

1. 1 <= text1.length, text2.length <= 1000
2. text1 和 text2 仅由小写英文字符组成。

来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/longest-common-subsequence

2.解题思路

方法一:动态规划

最长公共子序列问题是典型的二维动态规划问题。

假设字符串 text1text1 和 text2text2 的长度分别为 mm 和 nn,创建 m+1m+1 行 n+1n+1 列的二维数组 dpdp,其中 dp[i][j]dp[i][j] 表示 text1[0:i]text1[0:i] 和 text2[0:j]text2[0:j] 的最长公共子序列的长度。

上述表示中,text1[0:i]text1[0:i] 表示 text1text1 的长度为 ii 的前缀,text2[0:j]text2[0:j] 表示 text2text2 的长度为 jj 的前缀。

考虑动态规划的边界情况:

1. 当 i=0i=0 时,text1[0:i]text1[0:i] 为空,空字符串和任何字符串的最长公共子序列的长度都是 00,因此对任意 0≤j≤n0≤j≤n,有 dp[0][j]=0dp[0][j]=0;
2. 
3. 当 j=0j=0 时,text2[0:j]text2[0:j] 为空,同理可得,对任意 0≤i≤m0≤i≤m,有 dp[i][0]=0dp[i][0]=0。

因此动态规划的边界情况是:当 i=0i=0 或 j=0j=0 时,dp[i][j]=0dp[i][j]=0。

当 i>0i>0 且 j>0j>0 时,考虑 dp[i][j]dp[i][j] 的计算:

1. 当 text1[i−1]=text2[j−1]text1[i−1]=text2[j−1] 时,将这两个相同的字符称为公共字符,考虑 text1[0:i−1]text1[0:i−1] 和 text2[0:j−1]text2[0:j−1] 的最长公共子序列,再增加一个字符(即公共字符)即可得到 text1[0:i]text1[0:i] 和 text2[0:j]text2[0:j] 的最长公共子序列,因此 dp[i][j]=dp[i−1][j−1]+1dp[i][j]=dp[i−1][j−1]+1。
2. 
3. 当 text1[i−1]≠text2[j−1]text1[i−1]=text2[j−1] 时,考虑以下两项:
4. 
5.     text1[0:i−1]text1[0:i−1] 和 text2[0:j]text2[0:j] 的最长公共子序列;
6. 
7.     text1[0:i]text1[0:i] 和 text2[0:j−1]text2[0:j−1] 的最长公共子序列。
8. 
9. 要得到 text1[0:i]text1[0:i] 和 text2[0:j]text2[0:j] 的最长公共子序列,应取两项中的长度较大的一项,因此 dp[i][j]=max⁡(dp[i−1][j],dp[i][j−1])dp[i][j]=max(dp[i−1][j],dp[i][j−1])。

由此可以得到如下状态转移方程:

dp[i][j]={dp[i−1][j−1]+1,text1[i−1]=text2[j−1]max⁡(dp[i−1][j],dp[i][j−1]),text1[i−1]≠text2[j−1]dp[i][j]={dp[i−1][j−1]+1,max(dp[i−1][j],dp[i][j−1]),text1[i−1]=text2[j−1]text1[i−1]=text2[j−1]

最终计算得到 dp[m][n]dp[m][n] 即为 text1text1 和 text2text2 的最长公共子序列的长度。

3.代码实现

```java class Solution { public int longestCommonSubsequence(String text1, String text2) { int m = text1.length(), n = text2.length(); int[][] dp = new int[m + 1][n + 1]; for (int i = 1; i <= m; i++) { char c1 = text1.charAt(i - 1); for (int j = 1; j <= n; j++) { char c2 = text2.charAt(j - 1); if (c1 == c2) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[m][n]; } }
```

二、Leetcode 1035.不相交的线

1.题目

在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。

现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足:

1. nums1[i] == nums2[j]
2. 且绘制的直线不与任何其他连线(非水平线)相交。

请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。

以这种方法绘制线条,并返回可以绘制的最大连线数。

示例 1:

输入:nums1 = [1,4,2], nums2 = [1,2,4] 输出:2 解释:可以画出两条不交叉的线,如上图所示。 但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。

示例 2:

输入:nums1 = [2,5,1,2,5], nums2 = [10,5,2,1,5,2] 输出:3

示例 3:

输入:nums1 = [1,3,7,1,7,5], nums2 = [1,9,2,5,1] 输出:2

提示:

1. 1 <= nums1.length, nums2.length <= 500
2. 1 <= nums1[i], nums2[j] <= 2000

来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/uncrossed-lines

2.解题思路

方法一:动态规划

给定两个数组 nums1nums1 和 nums2nums2,当 nums1[i]=nums2[j]nums1[i]=nums2[j] 时,可以用一条直线连接 nums1[i]nums1[i] 和 nums2[j]nums2[j]。假设一共绘制了 kk 条互不相交的直线,其中第 xx 条直线连接 nums1[ix]nums1[ix] 和 nums2[jx]nums2[jx],则对于任意 1≤x≤k1≤x≤k 都有 nums1[ix]=nums2[jx]nums1[ix]=nums2[jx],其中 i1

上述 kk 条互不相交的直线分别连接了数组 nums1nums1 和 nums2nums2 的 kk 对相等的元素,而且这 kk 对相等的元素在两个数组中的相对顺序是一致的,因此,这 kk 对相等的元素组成的序列即为数组 nums1nums1 和 nums2nums2 的公共子序列。要计算可以绘制的最大连线数,即为计算数组 nums1nums1 和 nums2nums2 的最长公共子序列的长度。最长公共子序列问题是典型的二维动态规划问题。

假设数组 nums1nums1 和 nums2nums2 的长度分别为 mm 和 nn,创建 m+1m+1 行 n+1n+1 列的二维数组 dpdp,其中 dp[i][j]dp[i][j] 表示 nums1[0:i]nums1[0:i] 和 nums2[0:j]nums2[0:j] 的最长公共子序列的长度。

上述表示中,nums1[0:i]nums1[0:i] 表示数组 nums1nums1 的长度为 ii 的前缀,nums2[0:j]nums2[0:j] 表示数组 nums2nums2 的长度为 jj 的前缀。

考虑动态规划的边界情况:

1. 当 i=0i=0 时,nums1[0:i]nums1[0:i] 为空,空数组和任何数组的最长公共子序列的长度都是 00,因此对任意 0≤j≤n0≤j≤n,有 dp[0][j]=0dp[0][j]=0;
2. 
3. 当 j=0j=0 时,nums2[0:j]nums2[0:j] 为空,同理可得,对任意 0≤i≤m0≤i≤m,有 dp[i][0]=0dp[i][0]=0。

因此动态规划的边界情况是:当 i=0i=0 或 j=0j=0 时,dp[i][j]=0dp[i][j]=0。

当 i>0i>0 且 j>0j>0 时,考虑 dp[i][j]dp[i][j] 的计算:

1. 当 nums1[i−1]=nums2[j−1]nums1[i−1]=nums2[j−1] 时,将这两个相同的元素称为公共元素,考虑 nums1[0:i−1]nums1[0:i−1] 和 nums2[0:j−1]nums2[0:j−1] 的最长公共子序列,再增加一个元素(即公共元素)即可得到 nums1[0:i]nums1[0:i] 和 nums2[0:j]nums2[0:j] 的最长公共子序列,因此 dp[i][j]=dp[i−1][j−1]+1dp[i][j]=dp[i−1][j−1]+1。
2. 
3. 当 nums1[i−1]≠nums2[j−1]nums1[i−1]=nums2[j−1] 时,考虑以下两项:
4. 
5.     nums1[0:i−1]nums1[0:i−1] 和 nums2[0:j]nums2[0:j] 的最长公共子序列;
6. 
7.     nums1[0:i]nums1[0:i] 和 nums2[0:j−1]nums2[0:j−1] 的最长公共子序列。
8. 
9. 要得到 nums1[0:i]nums1[0:i] 和 nums2[0:j]nums2[0:j] 的最长公共子序列,应取两项中的长度较大的一项,因此 dp[i][j]=max⁡(dp[i−1][j],dp[i][j−1])dp[i][j]=max(dp[i−1][j],dp[i][j−1])。

由此可以得到如下状态转移方程:

dp[i][j]={dp[i−1][j−1]+1,nums1[i−1]=nums2[j−1]max⁡(dp[i−1][j],dp[i][j−1]),nums1[i−1]≠nums2[j−1]dp[i][j]={dp[i−1][j−1]+1,max(dp[i−1][j],dp[i][j−1]),nums1[i−1]=nums2[j−1]nums1[i−1]=nums2[j−1]

最终计算得到 dp[m][n]dp[m][n] 即为数组 nums1nums1 和 nums2nums2 的最长公共子序列的长度,即可以绘制的最大连线数。

fig1

3.代码实现

```java class Solution { public int maxUncrossedLines(int[] nums1, int[] nums2) { int m = nums1.length, n = nums2.length; int[][] dp = new int[m + 1][n + 1]; for (int i = 1; i <= m; i++) { int num1 = nums1[i - 1]; for (int j = 1; j <= n; j++) { int num2 = nums2[j - 1]; if (num1 == num2) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[m][n]; } }
```

三、Leetcode 53. 最大子序和

1.题目

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

输入:nums = [1] 输出:1

示例 3:

输入:nums = [5,4,-1,7,8] 输出:23

提示:

1. 1 <= nums.length <= 105
2. -104 <= nums[i] <= 104

来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/maximum-subarray

2.解题思路

方法一:动态规划

思路和算法

假设 numsnums 数组的长度是 nn,下标从 00 到 n−1n−1。

我们用 f(i)f(i) 代表以第 ii 个数结尾的「连续子数组的最大和」,那么很显然我们要求的答案就是:

max⁡0≤i≤n−1{f(i)}0≤i≤n−1max{f(i)}

因此我们只需要求出每个位置的 f(i)f(i),然后返回 ff 数组中的最大值即可。那么我们如何求 f(i)f(i) 呢?我们可以考虑 nums[i]nums[i] 单独成为一段还是加入 f(i−1)f(i−1) 对应的那一段,这取决于 nums[i]nums[i] 和 f(i−1)+nums[i]f(i−1)+nums[i] 的大小,我们希望获得一个比较大的,于是可以写出这样的动态规划转移方程:

f(i)=max⁡{f(i−1)+nums[i],nums[i]}f(i)=max{f(i−1)+nums[i],nums[i]}

不难给出一个时间复杂度 O(n)O(n)、空间复杂度 O(n)O(n) 的实现,即用一个 ff 数组来保存 f(i)f(i) 的值,用一个循环求出所有 f(i)f(i)。考虑到 f(i)f(i) 只和 f(i−1)f(i−1) 相关,于是我们可以只用一个变量 prepre 来维护对于当前 f(i)f(i) 的 f(i−1)f(i−1) 的值是多少,从而让空间复杂度降低到 O(1)O(1),这有点类似「滚动数组」的思想。

3.代码实现

```java class Solution { public int maxSubArray(int[] nums) { int pre = 0, maxAns = nums[0]; for (int x : nums) { pre = Math.max(pre + x, x); maxAns = Math.max(maxAns, pre); } return maxAns; } }


相关文章
|
6月前
【一刷《剑指Offer》】面试题 20:顺时针打印矩阵
【一刷《剑指Offer》】面试题 20:顺时针打印矩阵
|
6月前
【错题集-编程题】最长公共子序列(一)(动态规划 - LCS)
【错题集-编程题】最长公共子序列(一)(动态规划 - LCS)
|
6月前
|
算法
代码随想录算法训练营第五十六天 | LeetCode 647. 回文子串、516. 最长回文子序列、动态规划总结
代码随想录算法训练营第五十六天 | LeetCode 647. 回文子串、516. 最长回文子序列、动态规划总结
56 1
|
6月前
代码随想录Day45 动态规划13 LeetCode T1143最长公共子序列 T1135 不相交的线 T53最大子数组和
代码随想录Day45 动态规划13 LeetCode T1143最长公共子序列 T1135 不相交的线 T53最大子数组和
58 0
|
6月前
|
算法
【面试算法——动态规划 20】最长公共子序列&& 不相交的线
【面试算法——动态规划 20】最长公共子序列&& 不相交的线
|
算法
代码随想录算法训练营第五十三天 | LeetCode 1143. 最长公共子序列、1035. 不相交的线、53. 最大子数组和
代码随想录算法训练营第五十三天 | LeetCode 1143. 最长公共子序列、1035. 不相交的线、53. 最大子数组和
60 1
|
算法
代码随想录算法训练营第二天 | LeetCode 977.有序数组的平方、209.长度最小的子数组、59. 螺旋矩阵 II
代码随想录算法训练营第二天 | LeetCode 977.有序数组的平方、209.长度最小的子数组、59. 螺旋矩阵 II
55 0
|
算法 索引
代码随想录算法训练营第二天 |977.有序数组平方,209.长度最小的字数组,59.螺旋矩阵
代码随想录算法训练营第二天 |977.有序数组平方,209.长度最小的字数组,59.螺旋矩阵
|
C++
蓝桥杯 2240. 买钢笔和铅笔的方案数c++解法
蓝桥杯 2240. 买钢笔和铅笔的方案数c++解法
136 0
|
算法 索引
代码随想录算法训练营第二天| 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II ,总结
代码随想录算法训练营第二天| 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II ,总结