🧠
Optimized DP with Binary Indexed Tree / Segment Tree (O(n log n))
💡 This approach uses advanced data structures to optimize the DP solution to O(n log n), suitable for large inputs.
Intuition
Use coordinate compression and a Fenwick tree to quickly query and update LIS length and counts for all smaller elements.
Algorithm
- Coordinate compress nums to reduce range.
- Initialize Fenwick tree to store pairs of (length, count).
- For each number in nums, query Fenwick tree for max length and count of smaller numbers.
- Update Fenwick tree with new length and count for current number.
- At the end, find max length and sum counts from Fenwick tree.
💡 This approach combines DP with efficient queries and updates using Fenwick tree, a key advanced technique.
Recurrence:dp[i] = max query Fenwick tree for nums[j] < nums[i] + 1; count[i] = sum counts for max length
class FenwickTree:
def __init__(self, size):
self.size = size
self.tree = [(0, 0) for _ in range(size + 1)]
def update(self, i, length, count):
while i <= self.size:
cur_len, cur_count = self.tree[i]
if length > cur_len:
self.tree[i] = (length, count)
elif length == cur_len:
self.tree[i] = (cur_len, cur_count + count)
i += i & (-i)
def query(self, i):
max_len, total_count = 0, 0
while i > 0:
cur_len, cur_count = self.tree[i]
if cur_len > max_len:
max_len, total_count = cur_len, cur_count
elif cur_len == max_len:
total_count += cur_count
i -= i & (-i)
return max_len, total_count
def findNumberOfLIS(nums):
sorted_unique = sorted(set(nums))
def get_index(x):
# 1-based index for Fenwick tree
import bisect
return bisect.bisect_left(sorted_unique, x) + 1
fenw = FenwickTree(len(sorted_unique))
for num in nums:
idx = get_index(num) - 1
length, count = fenw.query(idx)
fenw.update(get_index(num), length + 1, max(count,1))
max_len, total_count = fenw.query(len(sorted_unique))
return total_count
# Example usage
if __name__ == '__main__':
print(findNumberOfLIS([1,3,5,4,7])) # Output: 2
Line Notes
class FenwickTree:Defines Fenwick tree data structure for efficient queries
def update(self, i, length, count):Updates Fenwick tree with new LIS length and count
def query(self, i):Queries Fenwick tree for max LIS length and count up to index i
for num in nums:Process each number in input array
fenw.update(get_index(num), length + 1, max(count,1))Update Fenwick tree with LIS info for current number
import java.util.*;
public class NumberOfLIS {
static class FenwickTree {
int size;
int[] length;
int[] count;
FenwickTree(int n) {
size = n;
length = new int[n + 1];
count = new int[n + 1];
}
void update(int i, int len, int cnt) {
while (i <= size) {
if (len > length[i]) {
length[i] = len;
count[i] = cnt;
} else if (len == length[i]) {
count[i] += cnt;
}
i += i & (-i);
}
}
int[] query(int i) {
int maxLen = 0, totalCount = 0;
while (i > 0) {
if (length[i] > maxLen) {
maxLen = length[i];
totalCount = count[i];
} else if (length[i] == maxLen) {
totalCount += count[i];
}
i -= i & (-i);
}
return new int[]{maxLen, totalCount};
}
}
public static int findNumberOfLIS(int[] nums) {
int n = nums.length;
TreeSet<Integer> set = new TreeSet<>();
for (int num : nums) set.add(num);
List<Integer> sorted = new ArrayList<>(set);
FenwickTree fenw = new FenwickTree(sorted.size());
for (int num : nums) {
int idx = Collections.binarySearch(sorted, num);
int[] res = fenw.query(idx); // idx is 0-based, query expects 1-based, so query(idx) is off by one
int length = res[0], count = res[1];
if (count == 0) count = 1;
fenw.update(idx + 1, length + 1, count);
}
int[] ans = fenw.query(sorted.size());
return ans[1];
}
public static void main(String[] args) {
System.out.println(findNumberOfLIS(new int[]{1,3,5,4,7})); // 2
}
}
Line Notes
class FenwickTree {Fenwick tree class to store LIS length and count
void update(int i, int len, int cnt)Update Fenwick tree nodes with LIS info
int[] query(int i)Query Fenwick tree for max LIS length and count
for (int num : nums)Iterate over input array
fenw.update(idx + 1, length + 1, count);Update Fenwick tree with current LIS data
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct FenwickTree {
int n;
vector<int> length, count;
FenwickTree(int size) : n(size), length(size + 1, 0), count(size + 1, 0) {}
void update(int i, int len, int cnt) {
while (i <= n) {
if (len > length[i]) {
length[i] = len;
count[i] = cnt;
} else if (len == length[i]) {
count[i] += cnt;
}
i += i & (-i);
}
}
pair<int,int> query(int i) {
int maxLen = 0, totalCount = 0;
while (i > 0) {
if (length[i] > maxLen) {
maxLen = length[i];
totalCount = count[i];
} else if (length[i] == maxLen) {
totalCount += count[i];
}
i -= i & (-i);
}
return {maxLen, totalCount};
}
};
int findNumberOfLIS(vector<int>& nums) {
vector<int> sorted = nums;
sort(sorted.begin(), sorted.end());
sorted.erase(unique(sorted.begin(), sorted.end()), sorted.end());
FenwickTree fenw(sorted.size());
for (int num : nums) {
int idx = (int)(lower_bound(sorted.begin(), sorted.end(), num) - sorted.begin());
auto [length, count] = fenw.query(idx);
if (count == 0) count = 1;
fenw.update(idx + 1, length + 1, count);
}
auto [maxLen, totalCount] = fenw.query((int)sorted.size());
return totalCount;
}
int main() {
vector<int> nums = {1,3,5,4,7};
cout << findNumberOfLIS(nums) << endl; // 2
return 0;
}
Line Notes
struct FenwickTree {Fenwick tree structure for efficient DP queries
void update(int i, int len, int cnt)Update Fenwick tree nodes
pair<int,int> query(int i)Query Fenwick tree for max LIS length and count
for (int num : nums)Process each element in nums
fenw.update(idx + 1, length + 1, count);Update Fenwick tree with LIS info
class FenwickTree {
constructor(size) {
this.size = size;
this.length = new Array(size + 1).fill(0);
this.count = new Array(size + 1).fill(0);
}
update(i, len, cnt) {
while (i <= this.size) {
if (len > this.length[i]) {
this.length[i] = len;
this.count[i] = cnt;
} else if (len === this.length[i]) {
this.count[i] += cnt;
}
i += i & (-i);
}
}
query(i) {
let maxLen = 0, totalCount = 0;
while (i > 0) {
if (this.length[i] > maxLen) {
maxLen = this.length[i];
totalCount = this.count[i];
} else if (this.length[i] === maxLen) {
totalCount += this.count[i];
}
i -= i & (-i);
}
return [maxLen, totalCount];
}
}
function findNumberOfLIS(nums) {
const sortedUnique = Array.from(new Set(nums)).sort((a,b) => a-b);
function getIndex(x) {
let left = 0, right = sortedUnique.length - 1;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (sortedUnique[mid] === x) return mid + 1;
else if (sortedUnique[mid] < x) left = mid + 1;
else right = mid - 1;
}
return left + 1;
}
const fenw = new FenwickTree(sortedUnique.length);
for (const num of nums) {
const idx = getIndex(num) - 1;
const [length, count] = fenw.query(idx);
fenw.update(getIndex(num), length + 1, Math.max(count, 1));
}
const [maxLen, totalCount] = fenw.query(sortedUnique.length);
return totalCount;
}
// Example usage
console.log(findNumberOfLIS([1,3,5,4,7])); // 2
Line Notes
class FenwickTree {Fenwick tree class for LIS length and count
update(i, len, cnt) {Update Fenwick tree nodes with LIS info
query(i) {Query Fenwick tree for max LIS length and count
for (const num of nums) {Iterate over input array
fenw.update(getIndex(num), length + 1, Math.max(count, 1));Update Fenwick tree with LIS data
Coordinate compression and Fenwick tree queries/updates take O(log n) per element, total O(n log n).
💡 For n=10^5, this approach is efficient enough to run within time limits.
Interview Verdict: Accepted
This is the optimal solution for large inputs and demonstrates mastery of advanced DP optimization.