LeetCode第 673 题:最长递增子序列的个数(C++)

673. 最长递增子序列的个数 - 力扣(LeetCode)

和这个是相似题目,只是本题要求的是个数(当然还是要寻找最长递增序列才能求个数)
LeetCode第 300 题:最长上升子序列(C++)_zj-CSDN博客

整体思路还是和上一题一样,双层循环dp,不过对于每个元素,我们都要记录以该元素为结尾的最长上升序列的长度以及序列的个数,还是看例子:

[1,3,5,4,7]

如果是第300题,我们的dp数组是这样的:

[1,2,3,3,4]

我们在计算出长度4的过程中,遍历了 4 前面的所有小于 4 对应元素的对应最长递增序列长度(有点拗口),比如长度 4 对应的数组元素为 7,我们遍历数组中元素7之前的所有比7小的元素(1,3,5,4),并找到这些元素对应的最长递增序列(1,2,3,3)的最大长度(3),然后3 + 1 = 4就是元素7对应的最大递增序列长度。

注意:上面的过程中我们虽然遍历到了两个3,但是我们只要知道最大的值为3即可,不需要知道有最大的值有几个。

这一题的区别就在于我们既要找最长递增序列,又要记录最长递增序列的长度,所以我们的dp数组元素设置为pair,第一项存储最大递增序列长度,第二项存储目前为止该最大长度出现的次数,所以计算应该是这样的:

[1,3,5,4,7]

dp数组:

[{1,1}, {2,1}, {3,1}, {3,1}, {4, 2}]

而且,最后返回结果的时候还需要遍历dp数组,找到最大长度,累加所有最大长度对应的个数,才是我们需要的结果。比如:

[2,2,2,2,2]

对应的dp数组为:

[{1,1}, {1,1}, {1,1}, {1,1}, {1,1}]

我们遍历之后知道最大长度为1,然后把长度为1的元素对应的个数全部累加起来得到5,最后返回5。

初步代码是这样的:

class Solution {
public:
    int findNumberOfLIS(vector& nums) {
        int n = nums.size();
        if(n < 2) return n;
        //f[i]存储的是以nums[i]为最大值的子序列长度以及该长度的序列的个数
        vector> f(n);
        f[0] = {1, 1};
        for(int i =1; i < n; ++i){
            int max_len = 0, cnt = 1;
            for(int j = 0; j < i; ++j){
                if(nums[j] < nums[i]){
                    if(f[j].first > max_len){
                        cnt = f[j].second;//取出个数
                        max_len = f[j].first;
                    }
                    else if(f[j].first == max_len)//有多个最大值
                        cnt += f[j].second;//累加个数
                }
            }
            f[i] = {max_len+1, cnt};
        }
        int len = max_element(f.begin(), f.end())->first;//默认会寻找pair的第一个位置最大的
        int res = 0;
        for(const auto& c : f){//可能有多个最大长度
            if(c.first == len)  res += c.second;
        }
        return res;
    }
};

可以换个写法,参考:C++ 动态规划 - 最长递增子序列的个数 - 力扣(LeetCode)

class Solution {
public:
    int findNumberOfLIS(vector& nums) {
        int n = nums.size();
        if(n < 2) return n;
        //f[i]存储的是以nums[i]为最大值的子序列长度以及该长度的序列的个数
        vector> f(n, {1,1});//初始化为1
        int max_len = 1;
        for(int i =1; i < n; ++i){
            for(int j = 0; j < i; ++j){
                if(nums[j] < nums[i]){
                    if(f[j].first +1 > f[i].first)
                        f[i] = {f[j].first+1, f[j].second};
                    else if(f[j].first+1 == f[i].first)//有多个最大值
                        f[i].second += f[j].second;//累加个数
                }
            }
            max_len = max(max_len, f[i].first);
        }
        int res = 0;
        for(const auto& c : f){//可能有多个最大长度
            if(c.first == max_len)  res += c.second;
        }
        return res;
    }
};

当然不使用pair使用一个新数组用来计数也是一样的道理。可以看题解:

最长递增子序列的个数 - 最长递增子序列的个数 - 力扣(LeetCode)

还有一个思路就是,300题里面我们通过贪心+二分不是查找到了一个最长递增子序列吗,我们可以遍历原nums,如果nums中的某个元素替换掉查找到的序列中的某个元素之后,序列依然是递增的,那最长递增子序列的个数就 + 1。不过这个思路是简单,代码却很难写,边界很难处理,要判断一个元素能不能替换掉某个元素之后仍然保持序列递增,需要判断的条件还挺多的。

f只有一个元素,两个元素,都要特殊处理,序列的最后两个元素也要特殊处理。

你可能感兴趣的:(leetcode)