代码随想录打卡Day43

今天第一道题和第三道题看了视频,第二道自己AC的,还是觉得第三道题有点难了。

300.最长递增子序列

这道题之前做过,但是不是用动态规划的思路做的(当时用的啥思路也忘干净了,无语(ˉ▽ˉ;)…),这道题我一开始构造的dp数组的含义是:在考虑下标在[0,i]范围内,所能得到的最长递增子序列的长度为dp[i],但是按照这个定义去做,做不出来,于是放弃,老老实实去看视频,这道题正确的构造为:以nums[i]结尾的情况下,所能得到的最长递增子序列的长度为dp[i]。在这个定义的基础上,递推公式就很好想了,用一个二重循环,外层循环用来得到最终的dp[i],内层循环用来维护更新dp[i],在内层循环中,只要nums[j] < nums[i],则dp[i] = max(dp[i], nums[j + 1]),为什么这里要用到max,而不是nums[j] + 1?考虑这么一个例子:
输入数组为[1, 2, 3, 4, 10, 4, 11],当i遍历到10时,dp[4] = 5,当i遍历到第二个4时,显而易见dp[5] = 4,当i遍历11时,前面的所有元素均满足nums[j] < nums[i],但是如果使用nums[j] + 1就会出问题,dp[6] = 5,但是显然正确答案应该是6,某个元素之前的所有元素均小于它,并不意味着前面的元素依然满足递增关系,所以需要用max来维护更新。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        //1.确定dp[i]的含义:以nums[i]结尾的情况下,所能获得的最长递增子序列的长度为dp[i]
        //2.确定递推公式dp[i] = max(dp[j] + 1, dp[i]);
        //3.dp数组初始化 dp[i] = 1;
        //4.确定遍历顺序:从小到大遍历
        //5.打印数组(省略)
        int m = nums.size();
        int result = 1;
        vector<int> dp(m, 1);
        for(int i = 1; i < m; i++){
            for(int j = 0; j < i; j++){
                if(nums[i] > nums[j])
                    dp[i] = max(dp[j] + 1, dp[i]);
            } 
            result = max(dp[i], result);
        }
        return result;
    }
};

674. 最长连续递增序列

这道题和上一题的区别在于上一题的递增子序列可以不连续,只要序列中的元素均来自输入数组就行,但是这道题要求必须要是输入数组中的某一个连续的递增子序列,因此在递推公式上有些许不同。下面举一个例子:
输入数组为[1, 3, 5, 4, 7],对于本题来说,以7结尾的最大连续递增子序列应当为[4,7],对应的dp数组应当为[1, 2, 3, 1, 2],这就说明,相比于上一题,本题在对待nums[j] >= nums[i]的情况时,不能再什么都不做,而是直接将dp[i]赋值为1(连续中断了,需要从头开始计数)。在对待nums[j] < nums[i]的情况时,由于要求是连续的,所以dp[i] = dp[j] + 1。除此以外其他地方的逻辑都是一样的。

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        //1.确定dp[i]的含义:以nums[i]结尾的情况下,所能获得的最长递增子序列的长度为dp[i]
        //2.确定递推公式dp[i] = nums[j] < nums[i] ? dp[j] + 1 : 1;
        //3.dp数组初始化 dp[i] = 1;
        //4.确定遍历顺序:从小到大遍历
        //5.打印数组(省略)
        int m = nums.size();
        int result = 1;
        vector<int> dp(m, 1);
        for(int i = 1; i < m; i++){
            for(int j = 0; j < i; j++)
                dp[i] = nums[j] < nums[i] ? dp[j] + 1 : 1;
            result = max(result, dp[i]);
        }
        return result;
    }
};

718. 最长重复子数组

这道题属实不会,看视频去了,这道题的二维dp数组确实不太会构造,视频里面dp[i][j]的定义和常规的想法还不太一样,视频说是为了代码的简洁,起初我不相信,我就按照“以nums1[i]结尾,以nums2[j]结尾的两个数组的最长重复子数组的长度为dp[i][j]”来定义的,后面发现这样定义确实代码会麻烦一点,不如视频里的定义简洁,但是第一次做谁也不会想到还要错位定义,然后我还是按照自己的定义做下去了,递推思路很简单,如果nums1[i] == nums[j],则最长重复子数组取决于他们前面的数字是否相等,直接在前一位的基础上加1即可。用我的定义有一个地方需要注意:可能在初始化的时候两个数组出现过相同元素,此时dp[i][0]或者dp[0][j]应该等于1,最后返回的值应当用一个变量result记录,如果在初始化的时候就已经出现了相同元素,result应该同步更新为1,为什么?因为有可能在初始化以后,在遍历两个数组的其他元素的过程中,再也没有 nums[i] 等于nums[j]的情况,如果不在初始化的时候就对result进行维护,result还是为0,最终返回0,但是在初始化的时候,两个数组是有相同元素的,应该返回1才对。但是如果按照视频来的话就不会出现这样的问题,属于是不听老人言吃亏在眼前了。

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        //1.确定dp[i][j]的含义:以一个数组以nums1[i]结尾, 另一个数组以nums[j]结尾的情况下,
        //  所能获得的最长重复子数组的长度为dp[i][j]
        //2.确定递推公式dp[i][j] = dp[i - 1][j - 1] + 1;
        //3.dp数组初始化 dp[0][j] = nums1[0] == nums2[j] ? 1 : 0;
        //              dp[i][0] = nums1[i] == nums2[0] ? 1 : 0;
        //4.确定遍历顺序:从小到大遍历
        //5.打印数组(省略)
        int m = nums1.size();
        int n = nums2.size();
        int result = 0;
        vector<vector<int>> dp(m, vector<int> (n, 0));
        //初始化
        for(int i = 0; i < m; i++){
            dp[i][0] = nums1[i] == nums2[0] ? 1 : 0;
            if(dp[i][0] > result) result = dp[i][0];  //赋值为1
        }
        for(int j = 0; j < n; j++){
            dp[0][j] = nums1[0] == nums2[j] ? 1 : 0;
            if(dp[0][j] > result) result = dp[0][j];  //赋值为1
        }  
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                if(nums1[i] == nums2[j])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                result = max(result, dp[i][j]);
            }
        }
        return result;
    }
};

下播!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值