代码随想录 | Day 53 - LeetCode 1143. 最长公共子序列、LeetCode 1035. 不相交的线、LeetCode 53. 最大子序和

        今天是子序列问题的延续,相对都比较简单。重点一方面是要分清楚与dp[i]对应的子序列是否要以nums[i]结尾,另一方面要判断好两种情况各自对应的dp数组值填充方法。


        第1题(LeetCode 1143. 最长公共子序列)相比day 52中第3题(LeetCode 718. 最长重复子数组),子序列不再要求是连续的,剩余地方都与其一样。这一变化反映在定义上,dp[i][j]就变成了text1[0: i - 1]与text2[0: j - 1]的最长公共子序列长度(不用[0 : i]和[0 : j]的理由与之前一致,避免初始化的麻烦)。

        对于text1[i - 1]与text2[j - 1]相等的情况,最长公共子序列长度相比没有text1[i - 1]和text2[j - 1]时就要增加一位,所以dp[i][j] = dp[i - 1][j - 1] + 1;而两者不相等时,因为子序列不再必须是连续的,所以此时的最大长度不需要归为1,而是维持之前的最大长度即可,之前的最大长度可能是dp[i - 1][j]或dp[i][j - 1],两者中取较大值。初始化方式、遍历顺序都与上一题一样。

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        vector> dp(text1.size() + 1, vector(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]) { // 不是text1[i] == text2[j]
                    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()];
    }
};

        实现中需要注意,因为为了初始化的方便而采用了额外多一行、一列的dp矩阵,所以与dp[i][j]对应的是text1[i - 1]和text2[j - 1]是否想等的判断,而不是text1[i]和text2[j]。


        第2题(LeetCode 1035. 不相交的线)中,“nums1[i] == nums2[j]”要求相等,“绘制的直线不与任何其他连线(非水平线)相交”要求有序,既相等又有序的要求就跟第1题是完全一样的。所以将这一题中的nums1、nums2分别看作第1题中的text1、text2即可,思路和实现完全一样。

class Solution {
public:
    int maxUncrossedLines(vector& nums1, vector& nums2) {
        vector> dp(nums1.size() + 1, vector(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 - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[nums1.size()][nums2.size()];
    }
};

        第3题(LeetCode 53. 最大子序和)在day 31中用贪心方法解决过,这里再用DP尝试。dp数组定义方面,将dp[i]定义为“以nums[i]结尾的连续子数组的最大和”。那么对于nums[i],就有两种选择。第1种是将自己拼接在前面的连续子数组之后,得到新的和dp[i - 1] + nums[i];第2种则是抛弃掉前面的连续子数组,从当前位置重新开始,得到新的和nums[i]。从这两种选择中取较大值作为当前位置的dp数值。而题目所求的“连续子数组的最大和”可能以任意位置的数字结尾,所以要在中途不断记录dp数组的最大值,作为最后的结果返回。

class Solution {
public:
    int maxSubArray(vector& nums) {
        int dp[nums.size()];
        dp[0] = nums[0];
        int ans = nums[0]; // nums[0]也可以初始化为INT_MIN
        for (int i = 1; i < nums.size(); ++i) {
            dp[i] = max(dp[i - 1] + nums[i], nums[i]);
            ans = max(ans, dp[i]);
        }
        return ans;
    }
};

你可能感兴趣的:(代码随想录,leetcode,算法,c++,动态规划,数据结构)