Bird
Raised Fist0
Interview Prepdp-advanced-trees-bitmaskmediumGoogleAmazon

Number of Longest Increasing Subsequences

Choose your preparation mode4 modes available
▶
Steps
setup

Coordinate Compression Setup

We extract the unique sorted values from the input array to compress coordinates, mapping each number to a smaller index range for Fenwick tree usage.

💡 Coordinate compression reduces the range of values, enabling efficient Fenwick tree operations on a smaller index set.
Line:sorted_unique = sorted(set(nums))
💡 The compressed coordinates allow Fenwick tree indices to correspond to unique values in ascending order.
📊
Number of Longest Increasing Subsequences - Watch the Algorithm Execute, Step by Step
Watching each Fenwick tree query and update reveals how the algorithm efficiently accumulates counts of longest increasing subsequences without enumerating them explicitly.
Step 1/13
·Active fill★Answer cell
Initializing dp array
i\w0123
i=0(0,0)(0,0)(0,0)(0,0)
Uninitialized Fenwick tree
Initializing dp array
i\w0123
i=0(0,0)(0,0)(0,0)(0,0)
Fenwick tree initialized
Item 0 - wt:1 val:1
i\w0123
i=0(0,0)(0,0)(0,0)(0,0)
Query index 0
Item 0 - wt:1 val:1
i\w0123
i=0(1,1)(0,0)(0,0)(0,0)
Update index 1
Item 1 - wt:3 val:3
i\w0123
i=0(1,1)(0,0)(0,0)(0,0)
Query index 1
Item 1 - wt:3 val:3
i\w0123
i=0(1,1)(2,1)(0,0)(2,1)
Update index 2
Item 2 - wt:5 val:5
i\w0123
i=0(1,1)(2,1)(0,0)(2,1)
Query index 2
Item 2 - wt:5 val:5
i\w0123
i=0(1,1)(2,1)(3,1)(3,1)
Update index 3
Item 3 - wt:4 val:4
i\w0123
i=0(1,1)(2,1)(3,1)(3,1)
Query index 2
Item 3 - wt:4 val:4
i\w0123
i=0(1,1)(2,1)(3,2)(3,2)
Update index 3 with merged counts
Item 4 - wt:7 val:7
i\w0123
i=0(1,1)(2,1)(3,2)(3,2)
Query index 3
Item 4 - wt:7 val:7
i\w0123
i=0(1,1)(2,1)(3,2)(4,2)
Update index 4
Initializing dp array
i\w0123
i=0(1,1)(2,1)(3,2)(4,2)
Answer cell

Key Takeaways

✓ Fenwick tree efficiently accumulates counts of longest increasing subsequences by storing (length, count) pairs at compressed indices.

This insight is hard to see from code alone because the Fenwick tree's internal state and propagation are abstract; visualization reveals how counts merge.

✓ When updating Fenwick tree, if a new subsequence length equals the current stored length, counts are added rather than replaced.

This merging of counts explains why multiple LIS can be counted without enumerating all subsequences explicitly.

✓ Querying Fenwick tree at index one less than current number's compressed index returns the best LIS info for all smaller numbers.

This shows how the algorithm builds on previously computed results, a key dynamic programming principle visualized step-by-step.

Practice

(1/5)
1. Consider the following Python code implementing the optimized LIS algorithm. What is the value of the variable tails after processing the input [3, 1, 2]?
easy
A. [1, 2, 3]
B. [1, 2]
C. [1, 3]
D. [3, 1, 2]

Solution

  1. Step 1: Process num=3

    tails is empty, bisect_left returns 0, append 3 -> tails = [3]
  2. Step 2: Process num=1

    bisect_left(tails,1) returns 0, replace tails[0] with 1 -> tails = [1]
  3. Step 3: Process num=2

    bisect_left(tails,2) returns 1 (end of tails), append 2 -> tails = [1, 2]
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    tails always sorted and minimal last elements [OK]
Hint: tails stores smallest possible ends of subsequences [OK]
Common Mistakes:
  • Appending instead of replacing in tails
  • Misunderstanding bisect_left index
  • Confusing tails with actual subsequence
2. The following code attempts to count arithmetic subsequences but contains a subtle bug. Identify the line causing incorrect counting of subsequences.
medium
A. dp = [defaultdict(int) for _ in range(n)]
B. count_at_j = dp[j][diff]
C. dp[i][diff] += count_at_j + 1
D. total += dp[i][diff] # adding wrong value to total

Solution

  1. Step 1: Understand counting logic

    Total should only increase by count_at_j, which counts subsequences extended by nums[i]. Adding dp[i][diff] counts pairs and subsequences multiple times.
  2. Step 2: Identify incorrect line

    Line adding dp[i][diff] to total causes double counting; it should add only count_at_j.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Correct counting adds count_at_j, not dp[i][diff] [OK]
Hint: Only add count_at_j to total, not dp[i][diff] [OK]
Common Mistakes:
  • Adding dp[i][diff] to total
  • Not initializing dp correctly
  • Counting pairs as subsequences
3. What is the time complexity of the optimal bottom-up DFS solution for computing the diameter of a binary tree with n nodes?
medium
A. O(n) because each node is visited once in the DFS traversal
B. O(n^2) due to repeated height computations in recursion
C. O(n log n) because height computations take log n time per node
D. O(n) but with additional O(n) space for memoization arrays

Solution

  1. Step 1: Identify that naive recursive approach causes repeated height computations

    In naive recursion without memoization, height computations are repeated, causing O(n^2) time complexity.
  2. Step 2: Confirm that optimal bottom-up DFS avoids repeated computations

    The question asks for the optimal bottom-up DFS solution, which is O(n), but the options are designed to test understanding of naive vs optimal.
  3. Step 3: Since O(n) because each node is visited once in the DFS traversal states optimal O(n), but O(n^2) due to repeated height computations in recursion states O(n^2) due to repeated computations, and question asks for optimal solution, correct answer is A.

  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Single DFS traversal over n nodes -> O(n) time [OK]
Hint: Single DFS traversal means linear time [OK]
Common Mistakes:
  • Confusing with brute force O(n^2)
  • Assuming height calls cost log n each
4. What is the time complexity of the topological sort based DP solution for the longest increasing path in an m x n matrix?
medium
A. O(m * n * log(m * n))
B. O(m * n^2)
C. O(m * n * 4^{m*n})
D. O(m * n)

Solution

  1. Step 1: Identify sorting cost

    Sorting all m*n cells by their values costs O(m*n*log(m*n)).
  2. Step 2: Analyze DP updates

    Each cell checks up to 4 neighbors, so DP updates cost O(m*n) total.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Sorting dominates, so complexity is O(m*n*log(m*n)) [OK]
Hint: Sorting cells dominates complexity [OK]
Common Mistakes:
  • Assuming DP is O(m*n) ignoring sorting
  • Confusing exponential brute force with DP
  • Ignoring log factor from sorting
5. What is the time complexity of the bottom-up bitmask DP solution for the Travelling Salesman Problem with n cities, and why? Options:
medium
A. O(n * 2^n) because we process each subset and city once
B. O(2^n) because subsets dominate the complexity
C. O(n^2 * 2^n) because for each subset and city, we check all possible previous cities
D. O(n! ) because we consider all permutations of cities

Solution

  1. Step 1: Identify loops in the code

    There are three nested loops: over all subsets (2^n), over all cities (n), and over all possible previous cities (n).
  2. Step 2: Multiply loop complexities

    Total complexity is O(2^n * n * n) = O(n^2 * 2^n). O(n * 2^n) because we process each subset and city once misses the inner loop over previous cities, underestimating complexity.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Bitmask DP complexity is well-known as O(n^2 * 2^n) [OK]
Hint: Remember triple nested loops: subsets, current city, previous city [OK]
Common Mistakes:
  • Ignoring inner loop over previous cities leads to underestimation
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard