How many longest increasing subsequences
Given a list of integers nums, return the number of longest strictly increasing subsequences.
Subsequences that use different positions count separately even if their values are equal.
Examples
| Input | Output | Why |
|---|---|---|
nums = [1,3,5,4,7] | 2 | 1, 3, 4, 7 and 1, 3, 5, 7. |
nums = [2,2,2,2,2] | 5 | The longest has length 1, and there are five of them. |
nums = [1,1,2,2] | 4 | Each 1 with each 2. |
Constraints
- 1 ≤ nums.length ≤ 2000
- −10⁶ ≤ nums[i] ≤ 10⁶
- The answer fits in a 32-bit integer
Hints
Hint 1
Keep two arrays: len[i], the longest increasing subsequence ending at i, and cnt[i], how many achieve that length.
Hint 2
For j < i with nums[j] < nums[i]: if len[j] + 1 is longer, take it and copy cnt[j]; if equal, add cnt[j].
Hint 3
Finally add cnt[i] over all i whose len[i] equals the overall maximum.
Where people lose marks
- Adding counts for every compatible j rather than only those achieving the best length.
- Returning the count at the last position instead of summing over all positions of maximum length.
Dry run on [1,3,5,4,7]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 5nums1031524374len10·1·2·3·4cnt10·1·2·3·4
Position 0 (1): longest ending here 1, reached in 1 way.
-
Step 2 / 5nums1031524374len1021·2·3·4cnt1011·2·3·4
Position 1 (3): longest ending here 2, reached in 1 way.
-
Step 3 / 5nums1031524374len102132·3·4cnt101112·3·4
Position 2 (5): longest ending here 3, reached in 1 way.
-
Step 4 / 5nums1031524374len10213233·4cnt10111213·4
Position 3 (4): longest ending here 3, reached in 1 way.
-
Step 5 / 5nums1031524374len1021323344cnt1011121324
Position 4 (7): longest ending here 4, reached in 2 ways. Maximum length 4; the counts at those positions add to 2.
How to think about it
Run the LIS table, but alongside len[i] keep cnt[i]: the number of increasing subsequences ending
at i that reach length len[i]. Each earlier compatible j offers subsequences of length len[j] + 1,
cnt[j] of them. A strictly longer offer replaces the count; an equally long one adds to it. The answer sums
cnt[i] over the positions that achieve the overall maximum length. This is the counting version of an
optimisation DP: track the optimum and the number of ways to reach it together.
Complexity. Time O(n²) · Space O(n)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def count_longest_increasing(nums):
n = len(nums)
length, count = [1] * n, [1] * n # best length ending at i, and how many reach it
for i in range(n):
for j in range(i):
if nums[j] < nums[i]:
if length[j] + 1 > length[i]:
length[i], count[i] = length[j] + 1, count[j]
elif length[j] + 1 == length[i]:
count[i] += count[j]
best = max(length)
return sum(c for l, c in zip(length, count) if l == best)#include <algorithm>
#include <vector>
long long solve(const std::vector<int>& nums) {
const int n = nums.size();
std::vector<int> len(n, 1);
std::vector<long long> cnt(n, 1);
for (int i = 0; i < n; ++i)
for (int j = 0; j < i; ++j) {
if (nums[j] >= nums[i]) continue;
if (len[j] + 1 > len[i]) { len[i] = len[j] + 1; cnt[i] = cnt[j]; }
else if (len[j] + 1 == len[i]) cnt[i] += cnt[j];
}
int best = *std::max_element(len.begin(), len.end());
long long total = 0;
for (int i = 0; i < n; ++i) if (len[i] == best) total += cnt[i];
return total;
}class Solution {
public long countLongestIncreasing(int[] nums) {
int n = nums.length, best = 0;
int[] len = new int[n];
long[] cnt = new long[n];
for (int i = 0; i < n; i++) {
len[i] = 1; cnt[i] = 1;
for (int j = 0; j < i; j++) {
if (nums[j] >= nums[i]) continue;
if (len[j] + 1 > len[i]) { len[i] = len[j] + 1; cnt[i] = cnt[j]; }
else if (len[j] + 1 == len[i]) cnt[i] += cnt[j];
}
best = Math.max(best, len[i]);
}
long total = 0;
for (int i = 0; i < n; i++) if (len[i] == best) total += cnt[i];
return total;
}
}function countLongestIncreasing(nums) {
const n = nums.length, len = new Array(n).fill(1), cnt = new Array(n).fill(1);
for (let i = 0; i < n; i++)
for (let j = 0; j < i; j++) {
if (nums[j] >= nums[i]) continue;
if (len[j] + 1 > len[i]) { len[i] = len[j] + 1; cnt[i] = cnt[j]; } // a longer chain
else if (len[j] + 1 === len[i]) cnt[i] += cnt[j]; // another chain as long
}
const best = Math.max(...len);
return len.reduce((s, l, i) => s + (l === best ? cnt[i] : 0), 0);
}fn solve(nums: &[i64]) -> i64 {
let n = nums.len();
let (mut len, mut cnt) = (vec![1i64; n], vec![1i64; n]);
for i in 0..n {
for j in 0..i {
if nums[j] >= nums[i] {
continue;
}
if len[j] + 1 > len[i] {
len[i] = len[j] + 1;
cnt[i] = cnt[j];
} else if len[j] + 1 == len[i] {
cnt[i] += cnt[j];
}
}
}
let best = *len.iter().max().unwrap();
(0..n).filter(|&i| len[i] == best).map(|i| cnt[i]).sum()
}import Data.Array
solve :: [Int] -> Int
solve nums = sum [c | (l, c) <- elems table, l == best]
where
n = length nums
a = listArray (0, n - 1) nums
-- table ! i = (longest length ending at i, number of subsequences reaching it)
table = listArray (0, n - 1) (map entry [0 .. n - 1]) :: Array Int (Int, Int)
entry i = foldl offer (1, 1) [table ! j | j <- [0 .. i - 1], a ! j < a ! i]
offer (l, c) (lj, cj)
| lj + 1 > l = (lj + 1, cj)
| lj + 1 == l = (l, c + cj)
| otherwise = (l, c)
best = maximum (map fst (elems table))
Variants to try
- O(n log n) with a Fenwick tree over values storing (length, count) pairs.
- Count the distinct value sequences instead of position sequences.
Next problems
The largest chain of divisors
Find the size of the largest subset of distinct positive integers in which every pair divides one way or the other. Sort first, and divisibility becomes a chain.
Open the problem → Medium20 minThe longest rise-then-fall subsequence
Return the length of the longest subsequence that strictly increases and then strictly decreases. Run the increasing table from both ends and join them at the peak.
Open the problem → Medium20 minThe longest alternating subsequence
Return the length of the longest subsequence whose successive differences alternate between positive and negative. Two numbers carry the whole table.
Open the problem →Write a solution and run it against the real test table.
Keep going
The largest chain of divisors
Find the size of the largest subset of distinct positive integers in which every pair divides one way or the other. Sort first, and divisibility becomes a chain.
Open the problem → Medium20 minThe longest rise-then-fall subsequence
Return the length of the longest subsequence that strictly increases and then strictly decreases. Run the increasing table from both ends and join them at the peak.
Open the problem → Medium20 minThe longest alternating subsequence
Return the length of the longest subsequence whose successive differences alternate between positive and negative. Two numbers carry the whole table.
Open the problem → Dynamic programmingThe pattern behind it
Write the recursion, notice the repeats, fill a table in an order that makes each entry final.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.