题目地址:
https://www.lintcode.com/problem/number-of-longest-increasing-subsequence/description
给定一个数组 A A A,求其最长严格单调增子序列的个数。
思路是动态规划。以下将”最长严格单调增子序列“简称为”最长上升子序列“。设
f
[
i
]
f[i]
f[i]是一个数对,其中
f
[
i
]
[
0
]
f[i][0]
f[i][0]是指以
A
[
i
]
A[i]
A[i]结尾的最长上升子序列的长度,
f
[
i
]
[
1
]
f[i][1]
f[i][1]是指以
A
[
i
]
A[i]
A[i]结尾的最长上升子序列的数量。则有:
f
[
i
]
[
0
]
=
max
{
max
j
<
i
{
f
[
j
]
[
0
]
}
+
1
,
1
}
f
[
i
]
[
1
]
=
max
{
∑
f
[
j
]
[
0
]
=
f
[
i
]
[
0
]
−
1
f
[
j
]
[
1
]
,
1
}
f[i][0]=\max\{\max_{j<i}\{f[j][0]\}+1,1\}\\f[i][1]=\max\{\sum_{f[j][0]=f[i][0]-1} f[j][1], 1\}
f[i][0]=max{j<imax{f[j][0]}+1,1}f[i][1]=max{f[j][0]=f[i][0]−1∑f[j][1],1}注意,在求
f
[
i
]
f[i]
f[i]的时候别忘了
i
i
i之前没有比
A
[
i
]
A[i]
A[i]小的数的情形,此时
A
[
i
]
A[i]
A[i]自己一个数作为最长上升子序列,其长度和数量都是
1
1
1。其当固定了
i
i
i的时候,可以从
0
∼
i
−
1
0\sim i-1
0∼i−1循环两遍求解,也可以只循环一遍:
1、我们用两个变量maxLen
和maxCount
分别表示能接在
A
[
i
]
A[i]
A[i]之前的最长上升子序列的长度和其数量,初始化为
0
0
0;
2、遍历
A
[
0
,
.
.
.
,
i
−
1
]
A[0,...,i-1]
A[0,...,i−1],判断
A
[
]
<
A
[
i
]
A[]<A[i]
A[]<A[i]之后,如果发现
f
[
]
[
0
]
f[][0]
f[][0]大于maxLen
,说明发现了一个更长的上升子序列可以接到
A
[
i
]
A[i]
A[i]后面,则更新maxLen
为
f
[
]
[
0
]
f[][0]
f[][0],并且更新maxCount
为
f
[
]
[
1
]
f[][1]
f[][1];如果发现
f
[
]
[
0
]
f[][0]
f[][0]等于maxLen
,说明又找到了一个长度最大的上升子序列,则让maxCount
加上
f
[
]
[
1
]
f[][1]
f[][1];如果发现
f
[
]
[
0
]
f[][0]
f[][0]小于maxLen
则不用理会,它不是已发现的最长的。
3、循环的同时可以用一个变量记录已经发现的最长上升子序列的长度,这样最后返回答案的时候,只需要返回
f
[
]
[
0
]
f[][0]
f[][0]等于那个长度的对应的数量即可。
代码如下:
public class Solution {
/**
* @param nums: an array
* @return: the number of longest increasing subsequence
*/
public int findNumberOfLIS(int[] nums) {
// Write your code here
if (nums == null || nums.length == 0) {
return 0;
}
// 这里的dp数组的含义就是上面f的含义
int[][] dp = new int[nums.length][2];
dp[0][0] = dp[0][1] = 1;
// 初始化全局最长上升子序列的长度为1
int globalMaxLen = 1;
for (int i = 1; i < nums.length; i++) {
int maxLen = 0, maxCount = 0;
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
if (dp[j][0] > maxLen) {
maxLen = dp[j][0];
maxCount = dp[j][1];
} else if (dp[j][0] == maxLen) {
maxCount += dp[j][1];
}
}
}
dp[i][0] = maxLen + 1;
// 如果i之前没有发现比A[i]小的数,则A[i]自成一个上升子序列,其数量是1
dp[i][1] = Math.max(1, maxCount);
globalMaxLen = Math.max(globalMaxLen, dp[i][0]);
}
int res = 0;
for (int i = 0; i < dp.length; i++) {
if (dp[i][0] == globalMaxLen) {
res += dp[i][1];
}
}
return res;
}
}
时间复杂度 O ( n 2 ) O(n^2) O(n2),空间 O ( n ) O(n) O(n)。