题目:
思路:
先来回顾动态规划求最长递增子序列长度
//动态规划
static int lengthOfLIS(int[] nums) {
if(nums == null || nums.length == 0){
return 0;
}
//整个问题的最优解,一开始至少有一个
int maxLen = 1;
int[] dp = new int[nums.length];
dp[0] = 1;
for(int i = 1; i < nums.length; i++){
//记录当前位置i之前的最大上升子序列长度
int currentMax = 0;
for(int j = 0; j < i; j++){
//只有比之前位置的元素大才进行currentMax 的更新
if(nums[i] > nums[j]){
currentMax = Math.max(currentMax, dp[j]);
}
}
//因为currentMax 记录当前位置i之前的最大上升子序列长度,所以+1,加的i这个
dp[i] = currentMax+1;
//更新整个问题的最优解,直接可以输出返回
maxLen = Math.max(maxLen, dp[i]);
}
return maxLen;
}
现在题目要求最长递增子序列的个数,要进行计数当目前最大长度为x有多个的时候,如果在之后出现最大长度为x+1的话,那么解就有多个
我们需要一个数组来记录当前位置i的(以nums[i]结尾的)最长递增子序列的解个数,如何计数?
显然nums[i] 要大于 nums[j]
当位置i是第一次记录的时候,它的计数值count[i]=count[j], 因为第一次记录就相当于nums[j]的最大递增子序列加上nums[i]而已,解个数不变,但长度+1;如果再次计算位置i的计数值的话就要count[i] += count[j]。怎么区分第一计算还是再次计算位置i。
重新观察原本的求最长递增子序列长度的做法,需要对其进行改造,如上代码。
计算当前位置i的最长递增子序列长度是求出i之前的一个max,然后+1,其实我们在做的事是求0-j,以及i这两部分形成的最长递增子序列,随着j的拓展,最终求到j-1。
改造:把这个步骤一步一步地求出来。前提条件始终是nums[i] > nums[j]
1.如果dp[j] + 1 > dp[i]那么就是i第一次被更新,要更新dp[i]。举例子可以发现基本上dp[j] == dp[i],因为j始终从0到j-1,而每个dp又有初识值1,所以dp[j] + 1 > dp[i];
dp[i] == dp[j]而nums[i] > nums[j],显然dp[i]可以更新
回看原来的做法,可以这么理解,currentMax+1就相当于dp[i], 什么时候更新currentMax,当遇到dp[j] >= currentMax的时候,这时候就如同改造后的dp[j] + 1 > dp[i]
2.如果dp[j] + 1 == dp[i],说明之前dp[i]之前已经求过一次了,并且dp[j]的值是有重复的,dp[i]不用更新
此时配合count数组,定义count[i]为以nums[i]结尾的最长递增子序列的组合数量。遇到1情况,count[i] = count[j],前面介绍过这种情况解个数不变,只是长度+1;遇到2情况,count[i] += count[j]
最后遍历一次dp,将dp[i] == 最长递增序列长度的count[i]相加就是答案
class Solution {
public int findNumberOfLIS(int[] nums) {
if (nums == null || nums.length == 0){
return 0;
}
int len = nums.length;
int[] dp = new int[len];
int[] count = new int[len];
//初始值为1,因为不管最长递增子序列还是解最差情况都有1种
Arrays.fill(dp, 1);
Arrays.fill(count, 1);
int maxLen = 1;
for (int i = 1; i < len; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
if (dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
//解个数不变,只是长度+1
count[i] = count[j];
} else if (dp[j] + 1 == dp[i]) {
count[i] += count[j];
}
}
}
maxLen = Math.max(maxLen, dp[i]);
}
int res = 0;
for (int i = 0; i < len; i++) {
if (dp[i] == maxLen){
res += count[i];
}
}
return res;
}
}
以题目示例1为例,[1,3,5,4,7]
5和4的dp值都是3,按照新的做法可以看到,求到7的时候,dp[4] = dp[2] +1 = 4,也就是在5的时候是第一次更新7的dp值,到4的时候,因为5和4的dp值是相同的,所以此时7的dp值已经== dp[3] + 1,能遇到这种情况也就说明了之前有相同的dp值,就是5和4的dp值,有重复即有更多解