目录:
https://leetcode.cn/problems/longest-common-subsequence/
思考:最长重复子数组那道题,不经要是公共子序列,还要是相邻才行。但是这道题所求公共子序列可以不相邻。所以这道题不是比较相邻数字,二十从前往后找。如果是公共子序列则+1。
随想录:
dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]
主要就是两大情况: text1[i - 1] 与 text2[j - 1]相同,text1[i - 1] 与 text2[j - 1]不相同
如果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]);
先看看dp[i][0]应该是多少呢?test1[0, i-1]和空串的最长公共子序列自然是0,所以dp[i][0] = 0;
同理dp[0][j]也是0。其他下标都是随着递推公式逐步覆盖,初始为多少都可以,那么就统一初始为0。
从递推公式,可以看出,有三个方向可以推出dp[i][j]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ErGSk2Oa-1689498665327)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aa8c4f0e-a6b2-42f2-9954-3d66e3e98555/Untitled.png)]
那么为了在递推的过程中,这三个方向都是经过计算的数值,所以要从前向后,从上到下来遍历这个矩阵。
以输入:text1 = “abcde”, text2 = “ace” 为例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AYVqXVbr-1689498665330)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/63aefbaa-5b76-41e6-bb04-3828a05796f3/Untitled.png)]
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 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]);
}
}
}
return dp[text1.size()][text2.size()];
}
};
https://leetcode.cn/problems/uncrossed-lines/
在两条独立的水平线上按给定的顺序写下 nums1
和 nums2
中的整数。
现在,可以绘制一些连接两个数字 nums1[i]
和 nums2[j]
的直线,这些直线需要同时满足满足:
nums1[i] == nums2[j]
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
示例 1:
!https://assets.leetcode.com/uploads/2019/04/26/142.png
输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
思考:这道题确实跟上道题差不多,就求个最长公共子序列。
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 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][j - 1], dp[i - 1][j]);
}
}
}
return dp[nums1.size()][nums2.size()];
}
};
随想录:本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!
正确的刷题顺序对解题很有帮助。
https://leetcode.cn/problems/maximum-subarray/
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
思考:用递归的方法去看,可以设dp[i]表示下标为i的位置,具有最大和的连续子数组。那么dp[i] = max(dp[i-1] + nums[i], dp[i]).
贪心方法:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int result = INT32_MIN;
int count = 0;
for (int i = 0; i < nums.size(); i++) {
count += nums[i];
if (count > result) result = count;
if (count <= 0 ) count = 0;
}
return result;
}
};
随想录:
动规五部曲如下:
确定dp数组(dp table)以及下标的含义
dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]。
确定递推公式
dp[i]只有两个方向可以推出来:
一定是取最大的,所以dp[i] = max(dp[i - 1] + nums[i], nums[i]);
dp数组如何初始化
从递推公式可以看出来dp[i]是依赖于dp[i - 1]的状态,dp[0]就是递推公式的基础。
dp[0]应该是多少呢?
根据dp[i]的定义,很明显dp[0]应为nums[0]即dp[0] = nums[0]。
确定遍历顺序
递推公式中dp[i]依赖于dp[i - 1]的状态,需要从前向后遍历。
举例推导dp数组
以示例一为例,输入:nums = [-2,1,-3,4,-1,2,1,-5,4],对应的dp状态如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uVkM9WdD-1689498665331)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7efb9205-5217-4a92-9910-e21d916ff629/Untitled.png)]
在回顾一下dp[i]的定义:包括下标i之前的最大连续子序列和为dp[i]。
那么我们要找最大的连续子序列,就应该找每一个i为终点的连续最大子序列。
所以在递推公式的时候,可以直接选出最大的dp[i]。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if (nums.size() == 0) return 0;
vector<int> dp(nums.size());
dp[0] = nums[0];
int result = dp[0];
for (int i = 1; i < nums.size(); i++) {
dp[i] = max(dp[i - 1] + nums[i], nums[i]); // 状态转移公式
if (dp[i] > result) result = dp[i]; // result 保存dp[i]的最大值
}
return result;
}
};
还是子序列
dp的解法比较直接,但是需要将情况分析清楚,然后dp数组的定义很重要。