Given an integer array nums, find the number of Longest Increasing Subsequences (LIS) in the array.
Input: nums = [1, 3, 5, 4, 7]
Output: 2
Explanation: There are two LIS of length 4:
[1, 3, 4, 7]
[1, 3, 5, 7].
Input: nums = [2, 2, 2, 2, 2]
Output: 5
Explanation: All elements are the same, so every single element can form an LIS of length 1. There are 5 such subsequences.
Input: nums = [10, 9, 2, 5, 3, 7, 101, 18]
Pre-requisite: Memoization approach to find the length of Longest Increasing Subsequence
The first step will be to create a DP table that will help in printing the Longest Increasing Subsequence.
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int longestStrChain(vector<string>& words) {
unordered_map<std::string, int> chains; // Stores the max chain length for each word
vector<std::string> sortedWords = words;
sort(sortedWords.begin(), sortedWords.end(), [](const std::string& a, const std::string& b) {
return a.length() < b.length(); // Sort words by length
});
for (const std::string& word : sortedWords) {
chains[word] = 1; // Initialize the chain length for the current word
for (int i = 0; i < word.length(); i++) {
std::string pred = word.substr(0, i) + word.substr(i + 1); // Generate predecessor by removing one character
if (chains.find(pred) != chains.end()) {
chains[word] = std::max(chains[word], chains[pred] + 1);
}
}
}
int maxChainLength = 0;
for (const auto& entry : chains) {
maxChainLength = std::max(maxChainLength, entry.second);
}
return maxChainLength;
}
};
int main() {
vector<int> nums = {10, 9, 2, 5, 3, 7, 101, 18};
// Creating an object of Solution class
Solution sol;
int lengthOfLIS = sol.LIS(nums);
cout << "The length of the LIS for the given array is: " << lengthOfLIS << endl;
return 0;
}
import java.util.*;
class Solution {
public int longestStrChain(String[] words) {
Map<String, Integer> chains = new HashMap<>(); // Stores the max chain length for each word
String[] sortedWords = Arrays.copyOf(words, words.length);
Arrays.sort(sortedWords, (a, b) -> a.length() - b.length()); // Sort words by length
for (String word : sortedWords) {
chains.put(word, 1); // Initialize the chain length for the current word
for (int i = 0; i < word.length(); i++) {
String pred = word.substring(0, i) + word.substring(i + 1); // Generate predecessor by removing one character
if (chains.containsKey(pred)) {
chains.put(word, Math.max(chains.getOrDefault(word, 0), chains.get(pred) + 1));
}
}
}
int maxChainLength = chains.values().stream().mapToInt(Integer::intValue).max().orElse(0);
return maxChainLength;
}
}
class Main {
public static void main(String[] args) {
int[] nums = {10, 9, 2, 5, 3, 7, 101, 18};
// Creating an object of Solution class
Solution sol = new Solution();
int lengthOfLIS = sol.LIS(nums);
System.out.println("The length of the LIS for the given array is: " + lengthOfLIS);
}
}
class Solution:
def longestStrChain(self, words: List[str]) -> int:
chains = {} # Stores the max chain length for each word
sorted_words = sorted(words, key=len) # Sort words by length
for word in sorted_words:
chains[word] = 1 # Initialize the chain length for the current word
for i in range(len(word)):
# Generate predecessor by removing one character
pred = word[:i] + word[i+1:]
if pred in chains:
chains[word] = max(chains[word], chains[pred] + 1)
return max(chains.values())
# Example usage
nums = [10, 9, 2, 5, 3, 7, 101, 18]
# Creating an object of Solution class
sol = Solution()
lengthOfLIS = sol.LIS(nums)
print("The length of the LIS for the given array is:", lengthOfLIS)
var longestStrChain = function(words) {
const chains = new Map(); // Stores the max chain length for each word
const sortedWords = words.slice().sort((a, b) => a.length - b.length); // Sort words by length
for (const word of sortedWords) {
chains.set(word, 1); // Initialize the chain length for the current word
for (let i = 0; i < word.length; i++) {
const pred = word.slice(0, i) + word.slice(i + 1); // Generate predecessor by removing one character
if (chains.has(pred)) {
chains.set(word, Math.max(chains.get(word) || 0, chains.get(pred) + 1));
}
}
}
return Math.max(...Array.from(chains.values())); // Return the maximum chain length
};
// Example usage
let nums = [10, 9, 2, 5, 3, 7, 101, 18];
// Creating an object of Solution class
let sol = new Solution();
let lengthOfLIS = sol.LIS(nums);
console.log("The length of the LIS for the given array is:", lengthOfLIS);
Q: Why do we need two arrays (dp[] and count[])?
A: dp[] finds the longest increasing subsequence length. count[] keeps track of how many ways we can form LIS of that length.
Q: Why do we sum count[i] for all indices where dp[i] == max_length?
A: The total number of LIS is the sum of all subsequences that reach the maximum LIS length.
Q: How would you reconstruct the actual LIS sequences instead of just counting them?
A: Use parent tracking to store LIS paths and backtrack to find sequences.
Q: Can this problem be solved using a graph-based approach?
A: Yes! Treat nums[] as nodes, with edges where nums[j] < nums[i], and compute longest paths in a DAG.