第五十六天| 第九章 动态规划 part14 1143. 最长公共子序列 1035. 不相交的线 53. 最大子序和
一、1143. 最长公共子序列
-
题目链接:
-
题目介绍:
-
思路:
-
本题和"最长重复子数组"区别在于**这里不要求是连续的了,但要有相对顺序**,即:"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
-
dp五部曲:
-
(1)确定dp数组及下标含义:
dp[i][j]:表示的是以下标i-1为结尾的text1和以下标j-1为结尾的text2的最长公共子序列的长度
-
(2)确定递推公式:
本题不再要求连续,因此dp[i][j]可以由三个方向推出
如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1; 如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。 即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
-
(3)初始化dp数组:
根据dp数组的含义,char1[0, i-1]和空数组的公共子序列长度为0;同理,char2[0, j-1]和空数组的公共子序列长度也为0。因此,dp[i][0] = 0,dp[0][j] = 0。其他位置在后续均可以覆盖,因此都初始化为0。
-
(4)确定遍历顺序:
- 根据递推公式可知是正序
-
-
和重复子数组(连续公共子序列)不同,本题的最终结果是在二维dp数组的最右下角,因为根据递推公式,如果不相同还是会根据左边的和上边的推导出来。
-
-
代码:
java
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
char[] char1 = text1.toCharArray();
char[] char2 = text2.toCharArray();
// (1)确定dp数组及下标含义
// dp[i][j]:表示的是以下标i-1为结尾的text1和以下标j-1为结尾的text2的最长公共子序列的长度
int[][] dp = new int[char1.length + 1][char2.length + 1];
// (3)初始化dp数组
// 根据dp数组的含义,char1[0, i-1]和空数组的公共子序列长度为0;同理,char2[0, j-1]和空数组的公共子序列长度也为0。因此,dp[i][0] = 0,dp[0][j] = 0。其他位置在后续均可以覆盖,因此都初始化为0。
// (4)确定遍历顺序
// 根据递推公式可知是正序
for (int i = 1; i <= char1.length; i++) {
for (int j = 1; j <= char2.length; j++) {
// (2)确定递推公式
// 本题不再要求连续,因此dp[i][j]可以由三个方向推出
if (char1[i-1] == char2[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
// 和重复子数组(连续公共子序列)不同,本题的最终结果是在二维dp数组的最右下角,因为根据递推公式,如果不相同还是会根据左边的和上边的推导出来。
return dp[char1.length][char2.length];
}
}
二、1035. 不相交的线
-
题目介绍:
-
在两条独立的水平线上按给定的顺序写下
nums1
和nums2
中的整数。现在,可以绘制一些连接两个数字
nums1[i]
和nums2[j]
的直线,这些直线需要同时满足满足:nums1[i] == nums2[j]
- 且绘制的直线不与任何其他连线(非水平线)相交。
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
示例 1:
输入:nums1 = [1,4,2], nums2 = [1,2,4] 输出:2 解释:可以画出两条不交叉的线,如上图所示。 但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。
-
-
思路:
- 套壳的"最长公共子序列"
- 思路和解法与上道题目是一样的
-
代码:
java
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int[][] dp = new int[nums1.length + 1][nums2.length + 1];
for (int i = 1; i <= nums1.length; i++) {
for (int j = 1; j <= nums2.length; j++) {
if (nums1[i-1] == nums2[j-1]) {
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[nums1.length][nums2.length];
}
}
三、53. 最大子序和
-
题目介绍:
-
相关企业
给你一个整数数组
nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
-
-
思路:
-
dp五部曲:
-
(1)确定dp数组及下标含义
dp[i]:表示的是以nums[i]为结尾的最大连续子序列之和
-
(2)确定递推公式
dp[i]只能由两个方向推出来: 一个是:因为是连续的所以是dp[i-1] + nums[i] 另一个是:当前nums[i]
-
(3)初始化dp数组
dp[0]:表示的是以nums[0]为结尾的最大连续子序列的和,所以dp[0] = nums[0]
-
(4)遍历顺序:正序
-
-
-
代码:
java
class Solution {
public int maxSubArray(int[] nums) {
if (nums == null || nums.length == 0) return 0;
int result = nums[0];
// (1)确定dp数组及下标含义
// dp[i]:表示的是以nums[i]为结尾的最大连续子序列之和
int[] dp = new int[nums.length];
// (3)初始化dp数组
// dp[0]:表示的是以nums[0]为结尾的最大连续子序列的和,所以dp[0] = nums[0].
dp[0] = nums[0];
// (4)遍历顺序:正序
for (int i = 1; i < nums.length; i++) {
// (2)确定递推公式
// dp[i]只能由两个方向推出来:
// 一个是:因为是连续的所以是dp[i-1] + nums[i]
// 另一个是:当前nums[i]
dp[i] = Math.max(dp[i-1] + nums[i], nums[i]);
if (dp[i] > result) result = dp[i];
}
return result;
}
}
总结:
- 子数组 :求解的就是**连续子序列**
- 子序列:没有强调的连续,只要是子序列即可