递增子序列问题

首先谈下,子串和子序列的区别,子串指必须是连续,子序列可以不连续,但是不能改变相对位置。本文所有的问题都是针对于子序列的。

例如:str = "abcdefg"

"abc","cdr"为子串,“acd”为子序列,“cad”啥都不是,由于相对位置发生改变。

问题一:递增子序列(leetcode491)

给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。

示例:

输入: [4, 6, 7, 7]
       输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]

大体思路:

对于每个节点,有选择和不选择两种情况。可以使用dfs方式进行遍历,遍历过程中判断当前结点和路径的前一个结点的大小,若当前结点大于等于前一个结点,则有选择和不选择当前结点两种情况。若小于后一个结点时,则只有不选择这一种情况。到最后一个结点的时候将当前路径插入到结构集即可。

此外由于存在重复元素,可能会出现重复结果的情况。

以上述输入为例,此时路径为[4, 6], 选择 第一个7 不选择第二个 7 ,不选择第一个7,选择第二个7的结果均为[4,6,7]。

对于该情况,可以删除第一种结果,当当前结点和路径的最后一个结点相同时,此时只需跳过不选择当前结点的选项。

代码如下:

class Solution {
    List<List<Integer>> result = new LinkedList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        process(nums, 0, new Stack<Integer>(), Integer.MIN_VALUE);
        return result;
    }
    public void process(int[] nums, int index, Stack<Integer> stack, int preValue){
        if(index == nums.length){
            if(stack.size() >= 2){
                result.add(new ArrayList<>(stack));
            }
            return;
        }
        if(nums[index] >= preValue){//选择nums[index]
            stack.push(nums[index]);
            process(nums, index + 1, stack, nums[index]);
            stack.pop();
        }
        if(nums[index] == preValue) {//跳过不选择nums[index]的分支
            return ;
        }
        //不选择nums[index]
        process(nums, index + 1, stack, preValue);
    }
}

 

问题二:最长递增子序列

给定一个整型数组height[i], 找到最长的递增子序列,返回子序列的长度。

依次获得以nums[i]结尾的最长递增子序列的长度记做dp[i],然后返回这一系列长度的最大值。

很显然的dp问题。dp[0] = 1;

dp[i] = max(dp[j] + 1) : dp[i] > dp[j],j=[0,i )

实现代码如下:

    public static int process(int[] height){
        int[] dp = new int[height.length];
        for(int i = 0; i < dp.length; i++){
            int preLength = 0;
            for(int j = 0; j < i; j++){
                if(height[i] > height[j]){
                    preLength = Math.max(preLength, dp[j]);
                }
            }
            dp[i] = preLength + 1;
        }
        int result = 0;
        for(int i = 0; i < dp.length; i++){
            result = Math.max(result, dp[i]);
        }
        return result;
    }

问题三最长递增子序列的个数(leetcode673)

给定一个整型数组nums[i], 返回最长的递增子序列的个数。

本题中的递增是严格递增。

解决方案:

改写问题二,由于该问题还要活得最长递增子序列的个数,因此增加一维数组count,count[i]用于描述以nums[i]结尾的最长递增子串的个数。

dp[i]获得之后应再次遍历[j, i)统计最长递增子串的个数。

count[i] += count[j] : dp[j] == preMaxLength,nums[i] > nums[j],j=[0,i)

代码实现如下:

class Solution {
    public int findNumberOfLIS(int[] nums) {
        int[] dp = new int[nums.length];//dp[i] 存储以nums[i]结尾的最长子序列的长度
        int[] count = new int[nums.length];//counts[i] 表示以nums[i]结尾最长子序列的个数
        int maxLength = 0;
        for(int i = 0; i < dp.length; i++){
            int preLength = 0;
            for(int j = 0; j < i; j++){
                if(nums[i] > nums[j]){
                    preLength = Math.max(preLength, dp[j]);
                }
            }
            for(int j = 0; j < i; j++){
                if(nums[i] > nums[j]){
                    count[i] += dp[j] == preLength ? count[j] : 0;
                }
            }
            if(preLength == 0){
                count[i] = 1;
            }
            dp[i] = preLength + 1;
            maxLength = Math.max(dp[i], maxLength);
        }
        int result = 0;
        for(int i = 0; i < dp.length; i++){
            if(maxLength == dp[i]){
                result += count[i];
            }
        }
        return result;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章