In this article, we will explore the problem of finding the number of longest increasing subsequences in a given integer array. This problem presents an interesting challenge, requiring a balance between understanding the concept and implementing an efficient solution. We will discuss the problem in detail, delve into two different approaches to solve it, and provide a challenge for those looking to push their problem-solving skills further.
If you’re just joining us, it might be helpful to catch up on the previous entries. Want to see more problem-solving techniques? Follow me here on my blog!
Understanding the Problem
The problem is to determine the number of longest strictly increasing subsequences in a given integer array. Given an array nums, we need to return the number of subsequences that are the longest and strictly increasing.
Example 1:
Input: nums = [1,3,5,4,7]
Output: 2
Explanation: The two longest increasing subsequences are [1, 3, 4, 7] and [1, 3, 5, 7].
Example 2:
Input: nums = [2,2,2,2,2]
Output: 5
Explanation: The length of the longest increasing subsequence is 1, and there are 5 increasing subsequences of length 1, so output 5.
Using Efficient Approach (Dynamic Programming with HashMap)
This approach utilizes dynamic programming combined with a hash map to store the lengths and counts of the longest increasing subsequences starting from each index. This method ensures that we efficiently compute the required values without redundant calculations.
def findNumberOfLIS(self, nums: List[int]) -> int:
dp = {}
lenLIS, res = 0, 0
for i in range(len(nums) - 1, -1, -1):
maxLen, maxCnt = 1, 1
for j in range(i + 1, len(nums)):
if nums[j] > nums[i]:
length, count = dp[j]
if length + 1 > maxLen:
maxLen, maxCnt = length + 1, count
elif length + 1 == maxLen:
maxCnt += count
if maxLen > lenLIS:
lenLIS, res = maxLen, maxCnt
elif maxLen == lenLIS:
res += maxCnt
dp[i] = [maxLen, maxCnt]
return res
Explanation
-
Initialization:
dp
is a dictionary where the key is the index and the value is a list containing the length of the longest increasing subsequence starting at that index and its count.lenLIS
keeps track of the length of the longest increasing subsequence found.res
keeps track of the number of such longest increasing subsequences.
-
Iterating from the end: We iterate from the end of the array to the beginning. For each index
i
, we initializemaxLen
andmaxCnt
to1
because the minimum length of LIS starting fromi
is1
. -
Inner Loop:
- We check all subsequent elements to see if they can extend the increasing subsequence starting at
i
. - If
nums[j] > nums[i]
, we updatemaxLen
andmaxCnt
based on the length and count stored atdp[j]
.
- We check all subsequent elements to see if they can extend the increasing subsequence starting at
-
Updating Results:
- After processing
i
, ifmaxLen
exceedslenLIS
, we updatelenLIS
and setres
tomaxCnt
. - If
maxLen
is equal tolenLIS
, we increment res bymaxCnt
.
- After processing
-
Storing Results: Store the computed
maxLen
andmaxCnt
for indexi
in thedp
dictionary.
Time Complexity: O(n^2)
, where n
is the length of the array. This is due to the nested loops iterating through the array.
Space Complexity: O(n)
, for storing the dp dictionary.
Using Efficient Approach 2 (Dynamic Programming with Arrays)
This approach uses dynamic programming with arrays to store the lengths and counts of the longest increasing subsequences. It leverages two arrays to keep track of the lengths of the LIS ending at each index and the count of such subsequences.
def findNumberOfLIS(self, nums: List[int]) -> int:
n = len(nums)
dp = [0 for i in range(n)]
count = [1 for i in range(n)]
ans = 0
for i in range(n):
temp = 0
for j in range(i):
if nums[j] < nums[i]:
if temp < dp[j]:
temp = dp[j]
count[i] = count[j]
elif dp[j] == temp:
count[i] += count[j]
dp[i] = temp + 1
if dp[i] > dp[ans]:
ans = i
sol = 0
for i in range(n):
if dp[i] == dp[ans]:
sol += count[i]
return sol
Explanation
-
Initialization:
dp
is an array wheredp[i]
represents the length of the LIS ending at indexi
.count
is an array wherecount[i]
represents the number of LIS ending at indexi
.ans
is used to keep track of the index of the longest LIS found.
-
Outer Loop: Iterate through each index
i
to compute the LIS ending ati
. -
Inner Loop:
- For each
i
, iterate through all previous indicesj
to check ifnums[j] < nums[i]
. - If
nums[j] < nums[i]
, update the temporary lengthtemp
and countcount[i]
accordingly.
- For each
-
Updating
dp
andans
:- Update
dp[i]
totemp + 1
. - If the new LIS length at
i
is greater than the length atans
, updateans
.
- Update
-
Calculating Result: Iterate through
dp
to count the number of LIS of maximum length found.
Time Complexity: O(n^2)
, due to the nested loops iterating through the array.
Space Complexity: O(n)
, for storing the dp
and count
arrays.
Conclusion
In this article, we explored the problem of finding the number of longest increasing subsequences in a given integer array. We discussed two efficient approaches using dynamic programming with a hash map and arrays.
Understanding these methods enhances your problem-solving skills and prepares you for tackling similar challenges in coding interviews and competitions.
Found this helpful? Follow me for more leetcode adventures! Questions? React out via email.