代码随想录算法训练营Day-43 动态规划10 | 300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

300.最长递增子序列

动规五部曲

dp数组含义🌟🌟🌟: 以nums[i]为结尾的递增子序列最大长度

注:**为什么要加上"以nums[i]为结尾"这个条件?**因为这样就可以在更新dp数组的时候,通过i,获取到nums[i]的值,而递推dp数组需要nums[i]的值进行比较,只有当nums[j]<nums[i]时,才进行递推更新。

**递推公式:**j从0到i-1,搜寻每一个nums[j]小于nums[i]的情况,一旦发现该情况,就更新当前的dp[i]为"当前dp[i]"和"满足nums[j]<nums[i]的dp[j]+1"中的最大值。

**初始化:**全部初始化为1,因为子序列长度最小为1,1就是初始状态。

遍历顺序:

外层遍历:dp[i]依赖于0~i-1状态推导过来,所以只能从前向后顺序遍历,从1遍历到nums.size()-1;

内层遍历:只需要遍历完0~i-1这些索引即可,顺序倒序不重要

cpp 复制代码
class Solution {
public:
    int lengthOfLIS(vector<int>& nums){
        if(nums.size()==1) return 1;
        //dp[i]含义:以nums[i]为结尾的递增子序列最大长度
        //初始化:全部初始化为1,因为至少递增子序列长度不小于1,因此1为起始状态
        vector<int> dp(nums.size(),1);
        
        //由于最终不一定以谁结尾的递增子序列最大长度最大,所以要时刻更新result为最大递增子序列长度
        int result = 0;

        //遍历顺序:外层遍历i,从1开始到最后,因为dp[0]一定为1,不用考虑
        //内层遍历j,从0到i-1,搜寻每一个nums[j]小于nums[i]的情况,一旦发现该情况,就更新当前的dp[i],确保满足该情况的dp[j]+1不大于dp[i]本身
        for(int i=1;i<nums.size();i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    dp[i] = max(dp[j]+1, dp[i]);
                }
            }
            //检查当前dp[i]是否超过result记录的最大长度,若超过则更新
            if(dp[i]>result) result = dp[i];
        }
        return result;

    }
};

674. 最长连续递增序列

本题和上题的区别就在于本题是"连续"数组,所以递推时只需考虑上一状态也就是i-1的nums对应值即可,如果nums[i]>nums[i-1],说明**以nums[i]结尾的连续递增子序列最大长度比以nums[i-1]结尾的长度大1,**故递推公式变为:

cpp 复制代码
if(nums[i]>nums[i-1]){
    dp[i] = dp[i-1]+1;
}

遍历顺序 也只需要单层遍历,因为不需要考虑所有前i种情况,只需要考虑前一个情况

cpp 复制代码
class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        if(nums.size()==1) return 1;

        vector<int> dp(nums.size(),1);
        int result = 0;

        for(int i=1;i<nums.size();i++){
            if(nums[i]>nums[i-1]){
                dp[i] = dp[i-1]+1;
            }
            if(result<dp[i]) result = dp[i];
        }
        return result;
    }
};

**注:**同样需要result记录以每个nums[i]结尾的连续递增子序列最大长度,保证其始终为最大的那个。


718. 最长重复子数组

动规五部曲

dp数组含义🌟🌟🌟: 以nums1[i-1]和nums2[j-1]为公共结尾的连续重复子序列最大长度

递推公式: 遍历0~nums1.size()和0~nums2.size(),一旦发现nums1[i-1]==nums2[j-1] ,就知道当前的dp[i][j]是在左上角的状态基础上加一,即dp[i][j] = dp[i-1][j-1]+1,为什么是左上角,因为i-1和j-1是新的公共结尾,所以i和j都需要回退1格。

初始化: 全部初始化为0,因为最左和最上列无意义,所以初始化为更新的初始状态0(要从0开始1个1个加长度),其余位置无所谓,因为会被覆盖,所以索性可以全初始化为0

遍历顺序:

先nums1还是nums2都可以遍历顺序为顺序,因为需要前面的i-1和j-1为基础。

cpp 复制代码
class Solution {
public:
    int findLength(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;
            }
        }

        int result = 0;
        for(int i = 1; i <= nums1.size(); i++){
            for(int j = 1; j<=nums2.size(); j++){
                if(dp[i][j]>result) result = dp[i][j];
            }
        }
        return result;
    }
};

相关推荐
ximu_polaris1 小时前
设计模式(C++)-行为型模式-解释器模式
c++·设计模式·解释器模式
AIpanda8882 小时前
数字员工助推AI销冠系统和AI提效软件系统,实现企业智能化转型与运营效率提升
算法
2401_897190552 小时前
【C++高阶系列】告别内查找局限:基于磁盘 I/O 视角的 B 树深度剖析与 C++ 泛型实现!
java·c++·算法
多加点辣也没关系2 小时前
数据结构与算法|第十一章:跳表
数据结构·算法
m2xgo2 小时前
ThreadPoolexecutor源码分析、C++11线程池实现
开发语言·c++
风筝在晴天搁浅2 小时前
字节/蚂蚁/美团/拼多多 LeetCode 165.比较版本号
java·leetcode
晚风叙码2 小时前
归并排序:从原理到非递归实现,一文搞定
数据结构·算法
悲伤小伞2 小时前
LeetCode 热题 100_3-128. 最长连续序列
c++·算法·leetcode·哈希算法
学困昇2 小时前
彻底搞懂 Linux 基础 IO:文件描述符、重定向、dup2、缓冲区一次讲透!
linux·运维·服务器·开发语言·c++