算法刷题打卡046 | 动态规划14
LeetCode 1143 最长公共子序列
题目链接:1143. 最长公共子序列 - 力扣(Leetcode)
前一天才做完最长重复子数组,这题就是在那题的基础上不要求公共部分连续。一开始的想法是,不要求连续的情况下,以按行遍历为例,dp数组的遍历到dp[i][j]且nums1[i]==nums2[j],需要一个额外的for循环遍历第i行j之前的dp值,取其中不为0的最大值,在它的基础上得到dp[i][j],这样的时间复杂度比较高,可能会超时。
但是在讲解中并不需要额外的遍历,而是考虑nums1[i] != nums2[j]时,dp[i][j]也可以延续之前已经匹配的结果,而不是像连续情况下直接跳过。在具体代码实现上,dp数组初始化为(m+1)*(n+1)的全零二维数组,可以让初始化更简洁。
class Solution {
public:int longestCommonSubsequence(string text1, string text2) {int m = text1.size();int n = text2.size();vector<vector<int>> dp(m+1, vector<int>(n+1));int result = 0;for(int i = 1; i <= m; i++){for(int j = 1; j <= n; j++){// i, j从1开始遍历,text取值要-1if(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]);result = max(result, dp[i][j]);}}return result;}
};
这样的时间和空间复杂度都是O(mn)。
LeetCode 1035 不相交的线
题目链接:1035. 不相交的线 - 力扣(Leetcode)
仔细分析题目可以发现这道题就是套着相交线外衣的最长公共子序列应用题,只需要把变量名改一改就能把上一题的代码直接用起来:
class Solution {
public:int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {// 其实就是用相交线包装起来的最长公共子序列int m = nums1.size(), n = nums2.size();vector<vector<int>> dp(m + 1, vector<int>(n + 1));int result = 0;for(int i = 1; i <= m; i++){for(int j = 1; j<= n; 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]);result = max(result, dp[i][j]);}}return result;}
};
LeetCode 53 最大子序和
题目链接:53. 最大子数组和 - 力扣(Leetcode)
这题要求的是连续子数组的最大和,dp[i]要么从dp[i-1]加上当前的nums[i]转移过来,要么就是加上dp[i-1]之后的结果反而变小,于是让nums[i]重新作为子数组的开头继续遍历:
class Solution {
public:int maxSubArray(vector<int>& nums) {int n = nums.size();// dp[i]:以nums[i]结尾的最大的连续子数组的和vector<int> dp(n);dp[0] = nums[0];// 结果初始化int result = dp[0];for(int i = 1; i < n; i++){// dp[i]只有两个来源,因为是连续子数组dp[i] = max(dp[i-1] + nums[i], nums[i]);result = max(result, dp[i]);}return result;}
};
总的时间复杂度和空间复杂度都是O(n),比之前的题目也稍微简单一些。