首先談下,子串和子序列的區別,子串指必須是連續,子序列可以不連續,但是不能改變相對位置。本文所有的問題都是針對於子序列的。
例如: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;
實現代碼如下:
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)統計最長遞增子串的個數。
代碼實現如下:
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;
}
}