「代码随想录算法训练营」第三十八天 | 动态规划 part11
1143. 最长公共子序列
题目链接:https://leetcode.cn/problems/longest-common-subsequence/
文章讲解:https://programmercarl.com/1143.最长公共子序列.html
题目难度:中等
视频讲解:https://www.bilibili.com/video/BV1ye4y1L7CQ
题目状态:有点思路,但细节有点混乱
思路:
维护一个二维动规数组dp[i][j],用来记录长度为i-1的字符串text1与长度为j-1的字符串text2的最长公共子序列,它的更新条件如下:
- 若
text1[i - 1] == text2[j - 1]
,则dp[i][j] = dp[i - 1][j - 1] + 1
; - 若
text1[i - 1] != text2[j - 1]
,则dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
。
其中为什么是i-1和j-1,这个问题和上一题是一样的。
代码:
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int len1 = text1.size();
int len2 = text2.size();
vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));
for(int i = 1; i <= len1; ++i) {
for(int j = 1; j <= len2; ++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[len1][len2];
}
};
1035. 不相交的线
题目链接:https://leetcode.cn/problems/uncrossed-lines/
文章讲解:https://programmercarl.com/1035.不相交的线.html
题目难度:中等
视频讲解:https://www.bilibili.com/video/BV1h84y1x7MP
题目状态:看题解
思路:
其本质就是寻找最长公共子序列,也就是上一题。
代码:
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
int len1 = nums1.size();
int len2 = nums2.size();
vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));
for(int i = 1; i <= len1; ++i) {
for(int j = 1; j <= len2; ++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[len1][len2];
}
};
53. 最大子数组和
题目链接:https://leetcode.cn/problems/maximum-subarray/
文章讲解:https://programmercarl.com/0053.最大子序和(动态规划).html
题目难度:中等
视频讲解:https://www.bilibili.com/video/BV19V4y1F7b5
题目状态:之前使用其他方法AC过
思路一:贪心算法
遍历整体数组,记录当前总和count
,若count
出现了负数,说明前面这些数组都可以删掉,从下一个元素开始重新遍历,最终返回最大的count
值。
代码一:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int count = 0;
int res = INT_MIN;
for(int i = 0; i < nums.size(); ++i) {
count += nums[i];
if(count > res) res = count;
if(count <= 0) count = 0;
}
return res;
}
};
思路二:动态规划
维护一个一维动规数组dp[i]
表示以i
元素结尾的最大值(注意是以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 maxSum = dp[0];
for(int i = 1; i < nums.size(); ++i) {
dp[i] = max(nums[i], dp[i - 1] + nums[i]);
maxSum = max(maxSum, dp[i]);
}
return maxSum;
}
};
392. 判断子序列
题目链接:https://leetcode.cn/problems/is-subsequence/
文章讲解:https://programmercarl.com/0392.判断子序列.html
题目难度:简单
视频讲解:https://www.bilibili.com/video/BV1tv4y1B7ym/
题目状态:使用其他方式通过,没有使用动规
思路一:
使用两个指针i
和j
分别遍历题目中的序列s
和t
,若遇到s[i] == t[j]
,则i++
,最后判断i
的位置,如果i
最后的位置是序列s
的末尾,表示s
遍历完了,也就是说在t
中找到了所有的s
。
代码一:
class Solution {
public:
bool isSubsequence(string s, string t) {
if(s.empty()) return true;
if(t.empty()) return false;
int i = 0, j = 0;
while(i < s.size() && j < t.size()) {
if(s[i] == t[j]) i++;
j++;
}
return i == s.size();
}
};
思路二:动态规划
维持一个二维动规数组dp[i][j]
表示在序列s
遍历到i - 1
,序列t
遍历到j - 1
时,这两者的相同子序列的长度。
- 当
s[i - 1] == t[j - 1]
时,动规数组将在前面的基础上加1 - 当两者不相等的时候,动规数组维持原状
代码二:
class Solution {
public:
bool isSubsequence(string s, string t) {
int sLen = s.size();
int tLen = t.size();
vector<vector<int>> dp(sLen + 1, vector<int>(tLen + 1, 0));
for(int i = 1; i <= sLen; ++i) {
for(int j = 1; j <= tLen; ++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];
}
}
if(dp[sLen][tLen] == sLen) return true;
else return false;
}
};