[Leetcode] 491. Increasing Subsequences 解题报告

题目

Given an integer array, your task is to find all the different possible increasing subsequences of the given array, and the length of an increasing subsequence should be at least 2 .

Example:

Input: [4, 6, 7, 7]
Output: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]

Note:

  1. The length of the given array will not exceed 15.
  2. The range of integer in the given array is [-100,100].
  3. The given array may contain duplicates, and two equal integers should also be considered as a special case of increasing sequence.

思路

1、DFS+backtracking:这是稍微有点变形的DFS题目:对于nums中的每个整数,如果符合条件,我们有两种选择:1)将它加入到结果中,然后进行DFS并且回溯;2)忽略它,直接进行DFS。为此我们在dfs的内部设立一个哈希表,表示已经尝试过的整数。这样对于具有重复整数的情况,比如例子中给出的[4, 6, 7, 7],当我们遇到第一个7之后,则包含7的所有升序组合都已经被输出了,那么当遇到第二个7的时候,我们就可以忽略。否则就生成重复结果了。

我发现DFS + BackTracking的题目往往有两种模板,一种是dfs中没有for循环的,也就是只调用DFS的下一层(有点像在dfs内部再进行dfs);另一种是dfs内含有从当前位置到末尾的for循环的,也就是在dfs内,平行的调用dfs(有点像dfs内部再进行一个bfs)。前一种模板适合无局部状态的情况,而后一种模板则比较适合需要记录局部状态的情况,例如本题目中我们需要记录已经采用过的整数。

2、前缀生成法:注意到一个升序序列总是可以由升序的前缀加上当前整数组成,所以我们可以采用递推的方式由升序的前缀生成所有的结果。例如对于本题目,我们初始的升序前缀是{},那么随着扫描的进行,结果的变化依次是:

当扫描到4之后,由升序前缀{{}}生成{4},然后升序前缀更新为{{}, {4}};

当扫描到6之后,由升序前缀{{}, {4}}生成{{6}, {4, 6}},然后升序前缀更新为{{}, {4}, {6}, {4, 6}};

当扫描到7之后,由升序前缀{{}, {4}, {6}, {4, 6}}生成{{7}, {4, 7}, {6, 7}, {4, 6, 7}},然后升序前缀更新为{{}, {4}, {6}, {4, 6}, {7}, {4, 7}, {6, 7}, {4, 6, 7}};

当再次扫描到7之后,由升序前缀{{}, {4}, {6}, {4, 6}, {7}, {4, 7}, {6, 7}, {4, 6, 7}}可以生成{{7}, {4, 7}, {6, 7}, {4, 6, 7}, {7, 7}, {4, 7, 7}, {6, 7, 7}, {4, 6, 7, 7}},然后升序前缀更新为{{}, {4}, {6}, {4, 6}, {7}, {4, 7}, {6, 7}, {4, 6, 7}, {7}, {4, 7}, {6, 7}, {4, 6, 7}, {7, 7}, {4, 7, 7}, {6, 7, 7}, {4, 6, 7, 7}}。注意到背景为灰色的部分是重复元素,它们在set中将会被去重。最后再筛选出长度大于等于2的升序序列即可。

代码

1、DFS+backtracking:

class Solution {
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        vector<vector<int>> ret;
        vector<int> line;
        dfs(nums, ret, line, 0);
        return ret;
    }
private:
    void dfs(vector<int> &nums, vector<vector<int>> &ret, vector<int> &line, int pos) {
        if (line.size() > 1) {
            ret.push_back(line);
        }
        unordered_set<int> hash;
        for (int i = pos; i < nums.size(); ++i) {
            if((line.empty() || nums[i] >= line.back()) && hash.find(nums[i]) == hash.end()) {
                line.push_back(nums[i]);
                dfs(nums, ret, line,i + 1);
                line.pop_back();
                hash.insert(nums[i]);       // the key point to avoid repeatation
            }
        }
    }
};

2、前缀生成法:

class Solution {
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        set<vector<int>> seqs = {vector<int>(0)};       // increasing prefix
        for (int i = 0; i < nums.size(); i++) {
            vector<vector<int>> built(seqs.size());     // get a copy of seqs
            std::copy(seqs.begin(), seqs.end(), built.begin());
            for (auto seq : built) {
                if (seq.empty() || nums[i] >= seq.back()) {
                    seq.push_back(nums[i]);
                    seqs.insert(seq);
                }
            }
        }
        vector<vector<int>> res;
        for (auto seq : seqs)
            if (seq.size() > 1) {
                res.push_back(seq);
            }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值