任务日期:7.19
题目一链接:1143. 最长公共子序列 - 力扣(LeetCode)
思路:本题的思路与最长重复子数组很像,唯一的区别在于这里不要求是连续的了,但要有相对顺序。本题先构造一个二维dp[i][j]数组,代表以下标i - 1 为结尾的text1和下标以j - 1为结尾的text2的公共子序列的长度,同时进行初始化都为零,然后分别遍历text1和text2数组,比较i - 1和j - 1的值,二者相同则dp加一,不同则取一个最大值,最后返回一个result。
代码:
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
vector<vector<int>> dp(text1.size() + 1,vector<int> (text2.size() + 1,0)); //dp[i][j]代表以下标i - 1 为结尾的text1和下标以j - 1为结尾的text2的公共子序列的长度
int result = 0;
//dp数组的初始化:dp[i][0]和dp[0][j]这两行的值都是零,已经初始化了
//确定dp数组递推公式和遍历顺序
for(int i = 1;i <= text1.size();i ++) { //右边边界能取等是因为比较的是下标为i - 1和j - 1的数组元素
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]); //此时dp数组的大小应该是以下标i - 2为结尾的text1和以下标为j - 1为结尾的text2的最大公共子序列和另一个类似的值得最大值
if(dp[i][j] > result) result = dp[i][j];
}
}
return result;
}
};
难点:1.本题多了一步if(text1[] != text2) dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);原因在于本题不要求连续,因此可以取除这俩点dp数组的最大值,而不是直接是零。
题目二链接:1035. 不相交的线 - 力扣(LeetCode)
思路:同最长子序列那道题。。。
代码:
class Solution {
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;
}
};
题目三链接:53. 最大子数组和 - 力扣(LeetCode)
思路:首先本题要求连续数组还要求子数组的和的最大值,因此可以确定dp[i] = max(dp[i - 1] + nums[i],nums[i]),dp[i]的含义:以i为结尾的最大子数组的和。
代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
vector<int> dp(nums.size() + 1,0); //以nums[i]为结尾的最大连续子序列的和,不是指原数组从第一个值到nums[i]
dp[0] = nums[0];
int result = 0;
result = dp[0];//将result初始化为dp[0]
for(int i = 1;i < nums.size();i ++) {
dp[i] = max(dp[i - 1] + nums[i],nums[i]); //要么将nums[i]加入当前连续子序列;要么从头开始计算当前连续子序列和
if(dp[i] > result) result = dp[i];
}
return result;
}
};
难点:1.边界问题:当len等于0时,直接返回nums[0]
2.result需要初始化为nums[0],这样确保当len等于1时答案正确。
3.最后返回result而不是dp[len - 1]的原因在于根据递推公式可得,dp[i]的最大值不一定是最后一个dp值。
补充:dp[i]的含义:以nums[i]为结尾的最大连续子序列的和(未必从零开始),而不是指原数组从nums[0]到nums[i].
题目四链接:392. 判断子序列 - 力扣(LeetCode)
思路:1.本题通过删除t里与s不相同的元素来求二者的最大重复子序列的长度。
2.如果二者最长相同子序列的长度等于s的长度,那么s是t的子序列。
代码:
class Solution {
public:
bool isSubsequence(string s, string t) {
vector<vector<int>> dp(s.size() + 1,vector<int>(t.size() + 1,0)); //以下标为i - 1为结尾的s和以j - 1为结尾的t之间最大重复子序列的长度
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]; //删掉t里面的当前元素时二者的最大重复子序列的长度公式
}
}
if(dp[s.size()][t.size()] == s.size()) return true;
else return false;
}
};
难点:
1.如果s[i - 1] != t[j - 1],那么此时dp[i][j]就等于dp[i][j - 1],因为t更长,而不能是max(dp[i][j - 1],dp[i - 1][j]).
2.最后的判断条件:
如果当前的最大重复子序列的长度等于s的长度,那么s就是t的子序列。