代码随想录53——动态规划:1143最长公共子序列、1035不相交的线、53最大子序和

文章目录

1.1143最长公共子序列

参考:代码随想录,1143最长公共子序列力扣题目链接

1.1.题目

代码随想录53——动态规划:1143最长公共子序列、1035不相交的线、53最大子序和

; 1.2.解答

本题和动态规划: 718. 最长重复子数组 区别在于这里 不要求是连续的了,但要有相对顺序,即:”ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。

继续动规五部曲分析如下:

1.确定dp数组(dp table)以及下标的含义

dp[i][j]:长度为 [0, i - 1]的字符串text1与长度为 [0, j - 1]的字符串text2的最长公共子序列为 dp[i][j]

同理这里的定义和昨天的题目一样,为了方便写递推公式,给dp数组添加了第0行、第0列。

2.确定递推公式

主要就是两大情况: text1[i - 1]text2[j - 1]相同, text1[i - 1]text2[j - 1]不相同

  • 如果 text1[i - 1]text2[j - 1]相同,那么找到了一个公共元素,所以 dp[i][j] = dp[i - 1][j - 1] + 1;
  • 如果 text1[i - 1]text2[j - 1]不相同,那就看看 text1[0, i - 2]text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]text2[0, j - 2]的最长公共子序列,取最大的。即: dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);

注意:这里并不是很理解。。。

代码如下:

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

3.dp数组如何初始化

先看看 dp[i][0]应该是多少呢?

test1[0, i-1]和空串的最长公共子序列自然是0,所以 dp[i][0] = 0;,同理 dp[0][j]也是0。

其他下标都是随着递推公式逐步覆盖,初始为多少都可以,那么就统一初始为0

代码:

vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));

4.确定遍历顺序

从递推公式,可以看出,有三个方向可以推出 dp[i][j],如图。那么为了在递推的过程中,这三个方向都是经过计算的数值,所以要从前向后,从上到下来遍历这个矩阵。

代码随想录53——动态规划:1143最长公共子序列、1035不相交的线、53最大子序和

5.举例推导dp数组

以输入: text1 = "abcde", text2 = "ace" 为例,dp状态如图

代码随想录53——动态规划:1143最长公共子序列、1035不相交的线、53最大子序和
最后给出代码如下,和昨天的题目一样,自己也是做了调整,代码中的 i/j表示的是 nums的索引,而不是dp数组的索引。
int longestCommonSubsequence(string text1, string text2)
{
    if(text1.empty() || text2.empty())
        return 0;

    int result = 0;

    vector<vector<int>> dp(text1.size()+1, vector<int>(text2.size()+1, 0));

    for(int i = 0; i < text1.size(); i++)
    {
        for(int j = 0; j < text2.size(); j++)
        {
            if(text1[i] == text2[j])
            {
                dp[i+1][j+1] = dp[i][j] + 1;
            }
            else
            {
                dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1]);
            }
            result = dp[i+1][j+1] > result ? dp[i+1][j+1] : result;
        }
    }

    return result;
}

注意:这道题感觉和昨天的题目非常相似,但是当目前的两个数字不相等的时候,为什么是取前面的最长序列的最大值,自己还不是特别明白。。。

2.1035不相交的线

参考:代码随想录,1035不相交的线力扣题目链接

2.1.题目

代码随想录53——动态规划:1143最长公共子序列、1035不相交的线、53最大子序和

; 2.2.解答

绘制一些连接两个数字 A[i] 和 B[j] 的直线,只要 A[i] == B[j],且直线不能相交!

直线不能相交, 这就是说明在字符串A中 找到一个与字符串B相同的子序列,且这个子序列不能改变相对顺序,只要相对顺序不改变,链接相同数字的直线就不会相交

拿示例一A = [1,4,2], B = [1,2,4]为例,相交情况如图:

代码随想录53——动态规划:1143最长公共子序列、1035不相交的线、53最大子序和
其实也就是说A和B的最长公共子序列是[1,4],长度为2。 这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面)

这么分析完之后,大家可以发现: 本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!那么本题就和我们刚刚讲过的这道题目 动态规划:1143.最长公共子序列 就是一样一样的了。

这里就不给出代码了,因为和上面的代码其实是完全一样的!

3.53最大子序和

参考:代码随想录,53最大子序和力扣题目链接

3.1.题目

代码随想录53——动态规划:1143最长公共子序列、1035不相交的线、53最大子序和

; 3.2.解答

1.确定dp数组(dp table)以及下标的含义

dp[i]包括下标i之前的 最大连续子序列和为 dp[i]。注意:这里一定是包含 nums[i]的,因为是连续的子序列。

2.确定递推公式

dp[i]只有两个方向可以推出来:

  • dp[i - 1] + nums[i],即: nums[i]加入当前连续子序列和
  • nums[i],即:从头开始计算当前连续子序列和

一定是取最大的,所以 dp[i] = max(dp[i - 1] + nums[i], nums[i]);

注意:以上两种方式,不管那种方式,都一定是包含 nums[i]的!这个要和dp数组的定义紧密联系到一起!

3.dp数组如何初始化

从递推公式可以看出来 dp[i]是依赖于 dp[i - 1]的状态, dp[0]就是递推公式的基础。根据dp[i]的定义,很明显 dp[0]应为 nums[0],即 dp[0] = nums[0]

4.确定遍历顺序

递推公式中 dp[i]依赖于 dp[i - 1]的状态,需要从前向后遍历。

5.举例推导dp数组

以示例一为例,输入: nums = [-2,1,-3,4,-1,2,1,-5,4],对应的dp状态如下

代码随想录53——动态规划:1143最长公共子序列、1035不相交的线、53最大子序和
最后给出代码如下,很简单
int maxSubArray(vector<int> &nums)
{
    if(nums.empty())
        return 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(nums[i], dp[i-1] + nums[i]);
        result = dp[i] > result ? dp[i] : result;
    }

    return result;
}

Original: https://blog.csdn.net/qq_42731705/article/details/127818958
Author: Cc1924
Title: 代码随想录53——动态规划:1143最长公共子序列、1035不相交的线、53最大子序和

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/657495/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球