遞增子序列問題

首先談下,子串和子序列的區別,子串指必須是連續,子序列可以不連續,但是不能改變相對位置。本文所有的問題都是針對於子序列的。

例如: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;
    }
}

 

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