首先谈下,子串和子序列的区别,子串指必须是连续,子序列可以不连续,但是不能改变相对位置。本文所有的问题都是针对于子序列的。
例如: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> result = new LinkedList<>();
public List> findSubsequences(int[] nums) {
process(nums, 0, new Stack(), Integer.MIN_VALUE);
return result;
}
public void process(int[] nums, int index, Stack 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;
}
}