代码随想录算法训练营第五十三天|1143.最长公共子序列、1035.不相交的线、53. 最大子序和

LeetCode 1143 最长公共子序列

题目链接:https://leetcode.cn/problems/longest-common-subsequence/

思路:

  • dp数组的含义
    dp[i][j]代表以i-1结尾的text1数组和以j-1结尾的text2数组最长的重复子数组大小

  • 递推公式
    本题有两种情况:
    1、text1[i - 1] == text2[j - 1]
    所以此时递推公式为:
    d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j] = dp[i-1][j-1]+1 dp[i][j]=dp[i1][j1]+1
    2、text1[i - 1] != text2[j - 1]
    例子:text1:abc text2:ace
    两种情况:
    因为c和e不相同,所以可以是abc和ac相比,得出公共子序列的长度,也可以是ab和ace相比
    所以此时递推公式是:
    d p [ i ] [ j ] = m a x ( d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j ] ) dp[i][j] = max(dp[i][j-1],dp[i-1][j]) dp[i][j]=max(dp[i][j1],dp[i1][j])

  • 初始化
    dp[i][0]和dp[0][j]显然都是没有意义的,即二维数组的第一行和第一列,将其全部初始化为0即可。其余数值因为会在递推公式中被覆盖,所以也都初始化为0,这样可以使得代码相对简洁。

  • 遍历顺序
    显然遍历是从前往后,从左到右

代码:

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]);
            }
        }

        // for(int i = 0; i < dp.size(); i++)
        // {
        //     for(int j = 0; j < dp[0].size(); j++)
        //         cout << dp[i][j] << " ";
        //     cout<< endl;
        // }
        // cout << endl;


        return dp[text1.size()][text2.size()];
    }
};

总结

对比最大重复子数组,本题少了个限制就是不需要是连续的。


LeetCode 1035 不相交的线

题目链接:https://leetcode.cn/problems/uncrossed-lines/

思路:

本题其实和上一题最大公共子序列是一样的,因为连线不能相交,代表了只能一直往后连,即不能改变相对两个数组的相对顺序。

代码:

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 - 1][j], dp[i][j - 1]);
            }
        }

        return dp[nums1.size()][nums2.size()];
    }
};

总结

能看出来和上一题一样就不难了


LeetCode 53 最大子序和

题目链接:https://leetcode.cn/problems/maximum-subarray/

思路:

  • dp数组的含义
    dp[i]代表包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]。

  • 递推公式
    显然,有递推公式: d p [ i ] = d p [ i − 1 ] + n u m s [ i ] dp[i]=dp[i-1]+nums[i] dp[i]=dp[i1]+nums[i]
    如果从0开始的连续子序列和小于当前的nums[i],那么 d p [ i ] = n u m s [ i ] dp[i]=nums[i] dp[i]=nums[i],这样可以从当前位置开始重新往后计算子序和。
    所以,递推公式为:
    d p [ i ] = m a x ( d p [ i − 1 ] + n u m s [ i ] , n u m s [ i ] ) dp[i]=max(dp[i-1]+nums[i],nums[i]) dp[i]=max(dp[i1]+nums[i],nums[i])
    例子:[-2,1,-3,4,-1,2,1,-5,4]
    n u m s [ 3 ] = 4 nums[3]=4 nums[3]=4前, d p [ 0 ] = − 2 , d p [ 1 ] = − 1 , d p [ 2 ] = − 2 dp[0]=-2,dp[1]=-1,dp[2]=-2 dp[0]=2,dp[1]=1,dp[2]=2,显然此时4前面的数加起来后的子序和小于当前数组值即4,所以应该从当前数组值开始重新往后计算子序和。

  • 初始化
    显然,由递推公式可知,需要知道dp[0]的值,所以 d p [ 0 ] = n u m s [ 0 ] dp[0]=nums[0] dp[0]=nums[0]

  • 遍历顺序
    从前往后

代码:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.size() == 1)    return nums[0];
        vector<int>dp(nums.size(), 0);
        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]);
            result = result > dp[i] ? result : dp[i];
        }

        return result;
    }
};

总结

要想清楚如何重新开始计算子序和


今日总结:

​继续学习子序列问题。

你可能感兴趣的:(代码随想录算法训练营,算法,leetcode,c++)