Problem

Find the length of the longest strictly increasing subsequence.

Example: [10, 9, 2, 5, 3, 7, 101, 18] → length is 4 (e.g., [2, 3, 7, 101])


Naive DP Approach

def lengthOfLIS(nums: List[int]) -> int:
    n = len(nums)
    dp = [1] * n
    
    for i in range(n):
        for j in range(i):
            if nums[j] < nums[i]:
                dp[i] = max(dp[i], dp[j] + 1)
    
    return max(dp)

Time: O(n²)
Space: O(n)


Optimal: Binary Search Approach

Maintain array ans where ans[i] = smallest tail of any increasing subsequence of length i+1.

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        ans = []
        
        def lower_bound(t):
            """Find first element >= t"""
            lo, hi = 0, len(ans)
            while lo < hi:
                mid = lo + (hi - lo) // 2
                if ans[mid] < t:
                    lo = mid + 1
                else:
                    hi = mid
            return hi
        
        for num in nums:
            idx = lower_bound(num)
            if idx >= len(ans):
                ans.append(num)
            else:
                ans[idx] = num
        
        return len(ans)

Time: O(n log n)
Space: O(n)


How It Works

Step-by-Step Example

Array: [3, 1, 6, 2, 4, 9, 5, 8, 7]

num=3: ans=[3]
num=1: idx=0 → ans=[1]
num=6: idx=1 → ans=[1,6]
num=2: idx=1 → ans=[1,2]
num=4: idx=2 → ans=[1,2,4]
num=9: idx=3 → ans=[1,2,4,9]
num=5: idx=3 → ans=[1,2,4,5]
num=8: idx=4 → ans=[1,2,4,5,8]
num=7: idx=4 → ans=[1,2,4,5,7]

Return: len(ans) = 5


Why This Works

ans[i] is always the smallest tail for subsequences of length i+1. When we see a new number:

  • If it’s larger than all tails: Append it (extend the longest subsequence)
  • If it’s smaller: Replace the first element ≥ it. This improves future chances without changing length.

Key: By always keeping the smallest possible tail, we maximize future extension opportunities.

Lower Bound

lower_bound(num) finds where num would be inserted in sorted ans.

  • Position ≥ length: Append (new longest)
  • Position < length: Replace (keep length, improve tail)