给定一个未排序的整数数组,找到最长递增子序列的个数。
示例 1:
输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:
输入: [2,2,2,2,2] 输出: 5 解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
注意: 给定的数组长度不超过 2000 并且结果一定是32位有符号整数。
思路:这道题采用动态规划,如果我们维护一个一维数组dp,且dp[i]表示第i个下标的元素的最长递增子序列的个数,这样定义的话动态规划的递推方程比较难找,因为比如这个例子:
[1,3,5,4,7]
dp[0]=1,dp[1]=1,dp[2]=1,dp[3]=1,dp[4]=2。因为还要考虑子序列长度的信息,对于dp[4]很难判断等于哪两个子序列的和(题目中是dp[2]和dp[3]),所以直观的我们还需要一个维护另外一个长度的数组len。所以基本思路如下:
我们维护两个一维数组,len和cnt,len[i]表示以nums[i]结尾的最长递增数组的长度,cnt[i]表示以nums[i]结尾的最长递增数组的个数。那么我们需要两层循环,外循环遍历数组nums,对于固定的nums[i],内循环判断在nums[i]之前的元素nums[j],如果nums[i]>nums[j],那么判断len[j]+1是否大于len[i],如果大于意味着找到了更长的子序列,那么len[i]=len[j]+1,且把cnt[i]=cnt[j],如果len[j]+1等于len[i],意味着出现了上图中例子的情况,我们找到了相同长度的两个子序列,那么更新cnt[i]为cnt[i]+cnt[j]。最后我们再次遍历数组nums,找到长度最大的len[i]对应的cnt[i]累加起来即可。
参考代码:
class Solution {
public:
int findNumberOfLIS(vector<int>& nums) {
vector<int> len(nums.size(), 1);
vector<int> cnt(nums.size(),1);
int res = 0;
int mx = 0;
int n = nums.size();
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
if ((len[j] + 1)==len[i]) {
cnt[i]+=cnt[j];
}
else if ((len[j] + 1) > len[i]) {
cnt[i] = cnt[j];
len[i] = len[j] + 1;
}
}
}
}
for (int i = 0; i < nums.size(); i++) {
if (len[i] >= mx) {
if (len[i] > mx) {
mx = len[i];
res = cnt[i];
}
else {
res += cnt[i];
}
}
}
return res;
}
};