medium Number of Longest Increasing Subsequence
Problem Statement
Given an integer array nums, return the number of longest increasing subsequences.
A subsequence is derived from the array by deleting some or no elements without changing the order of the remaining elements
Note: Sequences should be strictly increasing.
Examples
-
Example 1:
- Input: nums =
[2, 6, 4, 3, 5] - Expected Output:
2 - Explanation: The longest increasing subsequences are
[2, 3, 5]and[2, 4, 5], both having a length of 3. Therefore, there are two such subsequences.
- Input: nums =
-
Example 2:
- Input: nums =
[10, 9, 2, 5, 3, 7, 101, 18] - Expected Output:
4 - Explanation: The longest increasing subsequences are
[2, 3, 7, 18],[2, 5, 7, 18],[2, 3, 101], and[2, 5, 101], each having a length of 4. Thus, there are four such subsequences.
- Input: nums =
-
Example 3:
- Input: nums =
[1, 5, 2, 6, 3, 7] - Expected Output:
3 - Explanation: The longest increasing subsequences are
[1, 2, 3, 7],[1, 2, 6, 7], and[1, 5, 6, 7].
- Input: nums =
Constraints:
- 1 <= nums.length <= 2000
- -106 <= nums[i] <= 106
Try it yourself
Try solving this question here:
✅ Solution Number of Longest Increasing Subsequence
Problem Statement
Given an integer array nums, return the number of longest increasing subsequences.
A subsequence is derived from the array by deleting some or no elements without changing the order of the remaining elements
Note: Sequences should be strictly increasing.
Examples
-
Example 1:
- Input: nums =
[2, 6, 4, 3, 5] - Expected Output:
2 - Explanation: The longest increasing subsequences are
[2, 3, 5]and[2, 4, 5], both having a length of 3. Therefore, there are two such subsequences.
- Input: nums =
-
Example 2:
- Input: nums =
[10, 9, 2, 5, 3, 7, 101, 18] - Expected Output:
4 - Explanation: The longest increasing subsequences are
[2, 3, 7, 18],[2, 5, 7, 18],[2, 3, 101], and[2, 5, 101], each having a length of 4. Thus, there are four such subsequences.
- Input: nums =
-
Example 3:
- Input: nums =
[1, 5, 2, 6, 3, 7] - Expected Output:
3 - Explanation: The longest increasing subsequences are
[1, 2, 3, 7],[1, 2, 6, 7], and[1, 5, 6, 7].
- Input: nums =
Constraints:
- 1 <= nums.length <= 2000
- -106 <= nums[i] <= 106
Solution
To solve this problem, the Binary Indexed Tree (BIT) algorithm is a great choice due to its efficiency in handling prefix sums and updates. The key idea is to maintain a frequency and length count for subsequences ending at each index of the array. Using BIT allows us to efficiently calculate and update the number of valid subsequences that can be extended by adding the current number.
This approach works because BIT efficiently tracks cumulative sums and updates in logarithmic time, which is crucial for handling the dynamic nature of the problem. Given that we need to frequently update and query subsequence lengths, BIT ensures that these operations are performed quickly, making it a suitable and effective approach for solving this problem.
Step-by-Step Algorithm
-
Initialize Variables:
- n: Store the size of the input array
Nums. - nums: Copy the input array
Numsto use in sorting. - bit: Initialize a list of
BITNodeobjects (Binary Indexed Tree), each holdingmaxLengthandfrequency.
- n: Store the size of the input array
-
Create and Sort Index List:
- Indices: Create a list of indices
[0, 1, 2, ..., n-1]corresponding to elements innums. - Sort Indices: Sort
Indicesbased on values innums. If two elements are equal, prioritize the one with the higher index.- Sorting ensures that we process elements in increasing order. This helps us efficiently calculate and update the longest increasing subsequence up to each element.
- Indices: Create a list of indices
-
Process Each Element:
- Loop through each index in the sorted list:
- Get Maximum Length: Use
getMax(index)to find the maximum length of the LIS that ends before the current index. - Check for New Subsequence:
- If
maxLength == 0, start a new subsequence by callingupdate(index, 1, 1).
- If
- Update BIT:
- If
maxLength > 0, find the frequency of subsequences with this length usinggetFrequency(index, maxLength). - Call
update(index, maxLength + 1, frequency)to record a new subsequence with updated length and frequency.
- If
- Get Maximum Length: Use
- Loop through each index in the sorted list:
-
BIT Operations:
- getMax(index):
- Starts at
index + 1and moves upwards through the BIT by subtracting the last set bit (index -= index & -index). - During each step, it checks the
maxLengthstored at the current position in the BIT and updates the result with the maximum value found. - Returns the maximum length of any LIS that ends before the given index.
- Starts at
- getFrequency(index, maxLength):
- Similar to
getMax, it starts atindex + 1and moves upwards through the BIT. - During each step, it checks if the
maxLengthat the current position matches the targetmaxLength. If it does, the correspondingfrequencyis added to the result. - Returns the total count of LIS with the given
maxLengththat ends before the specified index.
- Similar to
- update(index, maxLength, frequency):
- Starts at
index + 1and moves downwards through the BIT by adding the last set bit (index += index & -index). - At each step, it compares the
maxLengthat the current position:- If the stored
maxLengthis less than the currentmaxLength, it updates bothmaxLengthandfrequency. - If the stored
maxLengthis equal to the currentmaxLength, it adds thefrequencyto the existing value.
- If the stored
- This operation updates the BIT to reflect the new subsequence information at the given index.
- Starts at
- getMax(index):
-
Final Result:
- Find the maximum length of any LIS in the array using
getMax(n-1). - Get the total number of LIS with this maximum length using
getFrequency(n-1, maxLength). - Return this frequency as the result.
- Find the maximum length of any LIS in the array using
Algorithm Walkthrough
Input: nums = [2, 6, 4, 3, 5]
-
Initialization:
- Input:
nums = [2, 6, 4, 3, 5] - Length
n = 5. bit: A list of 6BITNodeobjects, each initialized withmaxLength = 0andfrequency = 0.
- Input:
-
Create and Sort Index List:
- Indices = [0, 1, 2, 3, 4] (indices of
nums). - After sorting
indicesusing custom comparatorcmp, based on values innums:- Indices = [0, 3, 2, 4, 1]
- Values at these indices:
[2, 3, 4, 5, 6].
- Indices = [0, 1, 2, 3, 4] (indices of
-
Process Each Element:
-
Index 0 (
nums[0] = 2):- Get Maximum Length (
getMax(0)):- Start at index 1 (
index + 1). - Traverse BIT: Check position 1 (0), position 0 (stop).
- Result:
maxLength = 0.
- Start at index 1 (
- Since
maxLength == 0, start a new subsequence: - Update BIT (
update(0, 1, 1)):- Start at index 1.
- Traverse BIT: Update position 1 (1,1), position 2 (1,1), position 4 (1,1).
- BIT after update:
[0, (1,1), (1,1), (0,0), (1,1), (0,0)].
- Get Maximum Length (
-
Index 3 (
nums[3] = 3):- Get Maximum Length (
getMax(3)):- Start at index 4 (
index + 1). - Traverse BIT: Check position 4 (1), position 0 (stop).
- Result:
maxLength = 1.
- Start at index 4 (
- Get Frequency (
getFrequency(3, 1)):- Start at index 4.
- Traverse BIT: Check position 4 (1), position 0 (stop).
- Result:
frequency = 1.
- Update BIT (
update(3, 2, 1)):- Start at index 4.
- Traverse BIT: Update position 4 (2,1), position 8 (stop).
- BIT after update:
[0, (1,1), (1,1), (0,0), (2,1), (0,0)].
- Get Maximum Length (
-
Index 2 (
nums[2] = 4):- Get Maximum Length (
getMax(2)):- Start at index 3 (
index + 1). - Traverse BIT: Check position 3 (0), position 2 (1), position 0 (stop).
- Result:
maxLength = 1.
- Start at index 3 (
- Get Frequency (
getFrequency(2, 1)):- Start at index 3.
- Traverse BIT: Check position 3 (0), position 2 (1), position 0 (stop).
- Result:
frequency = 1.
- Update BIT (
update(2, 2, 1)):- Start at index 3.
- Traverse BIT: Update position 3 (2,1), position 4 (2,2), position 8 (stop).
- BIT after update:
[0, (1,1), (1,1), (2,1), (2,2), (0,0)].
- Get Maximum Length (
-
Index 4 (
nums[4] = 5):- Get Maximum Length (
getMax(4)):- Start at index 5 (
index + 1). - Traverse BIT: Check position 5 (0), position 4 (2), position 0 (stop).
- Result:
maxLength = 2.
- Start at index 5 (
- Get Frequency (
getFrequency(4, 2)):- Start at index 5.
- Traverse BIT: Check position 5 (0), position 4 (2), position 0 (stop).
- Result:
frequency = 2.
- Update BIT (
update(4, 3, 2)):- Start at index 5.
- Traverse BIT: Update position 5 (3,2), position 6 (stop).
- BIT after update:
[0, (1,1), (1,1), (2,1), (2,2), (3,2)].
- Get Maximum Length (
-
Index 1 (
nums[1] = 6):- Get Maximum Length (
getMax(1)):- Start at index 2 (
index + 1). - Traverse BIT: Check position 2 (1), position 0 (stop).
- Result:
maxLength = 1.
- Start at index 2 (
- Get Frequency (
getFrequency(1, 1)):- Start at index 2.
- Traverse BIT: Check position 2 (1), position 0 (stop).
- Result:
frequency = 1.
- Update BIT (
update(1, 4, 2)):- Start at index 2.
- Traverse BIT: Update position 2 (2,1), position 4 (2,3), position 6 (stop).
- BIT after update:
[0, (1,1), (2,1), (2,1), (2,3), (3,2)].
- Get Maximum Length (
-
-
Final Result:
- Get Maximum Length (
getMax(4)):- Start at index 5 (
index + 1). - Traverse BIT: Check position 5 (2), position 4 (3), position 0 (stop).
- Result:
maxLength = 3.
- Start at index 5 (
- Get Frequency (
getFrequency(4, 3)):- Start at index 5.
- Traverse BIT: Check position 4 (2), position 0 (stop).
- Result:
frequency = 2.
- Final Result: Return
2as the number of longest increasing subsequences.
- Get Maximum Length (
Code
import java.util.*;
public class Solution {
// Define a class to represent each node in the BIT
class BITNode {
int frequency; // frequency of subsequences
int maxLength; // maximum length of increasing subsequence
BITNode() {
frequency = 0;
maxLength = 0;
}
}
private int n; // length of the input array
private List<BITNode> bit; // Binary Indexed Tree (BIT) to store max lengths and frequencies
private List<Integer> nums; // the input array
// Comparator to sort indices based on values in nums and then by index
private boolean cmp(int index1, int index2) {
if (nums.get(index1) != nums.get(index2)) {
return nums.get(index1) < nums.get(index2);
}
return index1 > index2;
}
public int findNumberOfLIS(List<Integer> Nums) {
this.n = Nums.size(); // get the size of the input array
this.nums = Nums; // store the input array
// Create a list of indices
List<Integer> indices = new ArrayList<>();
for (int i = 0; i < n; i++) indices.add(i);
// Sort indices using the custom comparator
indices.sort((a, b) -> cmp(a, b) ? -1 : 1);
// Initialize BIT with n+1 nodes
bit = new ArrayList<>();
for (int i = 0; i <= n; i++) bit.add(new BITNode());
// Traverse through the sorted indices
for (int index : indices) {
int maxLength = getMax(index); // Get max length of increasing subsequence ending before this index
if (maxLength == 0) {
// If no subsequence is found, start a new one
update(index, 1, 1);
continue;
}
int frequency = getFrequency(index, maxLength); // Get frequency of subsequences with max length
update(index, maxLength + 1, frequency); // Update BIT with new max length and frequency
}
int maxLength = getMax(n - 1); // Get max length of increasing subsequence in the whole array
return getFrequency(n - 1, maxLength); // Return the frequency of subsequences with max length
}
private int getMax(int index) {
index++;
int result = 0;
while (index > 0) {
result = Math.max(result, bit.get(index).maxLength); // Update result with the max length found
index -= index & -index; // Move to the parent node
}
return result;
}
private int getFrequency(int index, int maxLength) {
index++;
int result = 0;
while (index > 0) {
if (bit.get(index).maxLength == maxLength) {
result += bit.get(index).frequency; // Accumulate frequency of matching subsequences
}
index -= index & -index; // Move to the parent node
}
return result;
}
private void update(int index, int maxLength, int frequency) {
index++;
while (index <= n) {
if (bit.get(index).maxLength < maxLength) {
bit.get(index).maxLength = maxLength; // Update max length
bit.get(index).frequency = frequency; // Set the frequency
} else if (bit.get(index).maxLength == maxLength) {
bit.get(index).frequency += frequency; // Increment frequency for existing max length
}
index += index & -index; // Move to the next node
}
}
public static void main(String[] args) {
Solution solution = new Solution();
// Example 1
List<Integer> nums1 = Arrays.asList(2, 6, 4, 3, 5);
int result1 = solution.findNumberOfLIS(nums1);
System.out.println("Number of Longest Increasing Subsequences: " + result1); // Expected output: 2
// Example 2
List<Integer> nums2 = Arrays.asList(10, 9, 2, 5, 3, 7, 101, 18);
int result2 = solution.findNumberOfLIS(nums2);
System.out.println("Number of Longest Increasing Subsequences: " + result2); // Expected output: 4
// Example 3
List<Integer> nums3 = Arrays.asList(1, 5, 2, 6, 3, 7);
int result3 = solution.findNumberOfLIS(nums3);
System.out.println("Number of Longest Increasing Subsequences: " + result3); // Expected output: 3
}
}
Complexity Analysis
Time Complexity
- Sorting the indices takes
. - For each index in the sorted list, the algorithm performs two operations:
- Getting the maximum length: This operation is
since it traverses the Binary Indexed Tree. - Getting the frequency: This is also
for the same reason. - Updating the BIT: The update operation is
as well.
- Getting the maximum length: This operation is
- Since these operations are performed for each element in the array, the overall time complexity is
.
Space Complexity
- The space complexity is
because of the additional space required for the BIT and the sorted index list. - The input array itself is
.
🤖 Don't fully get this? Learn it with Claude
Stuck on Number of Longest Increasing Subsequence? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Progressively stronger hints — you still solve it.
I'm working on the problem **Number of Longest Increasing Subsequence** (DSA). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
See the technique, not just code.
Explain the optimal approach to **Number of Longest Increasing Subsequence** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Catch bugs, edge cases, sub-optimality.
I'll paste my solution to **Number of Longest Increasing Subsequence**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Lock in recognition with look-alikes.
Give me 2 problems that use the SAME underlying pattern as **Number of Longest Increasing Subsequence**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.