cconnected.
Understand the pattern. Find your own way.Find your starting point
Your learning feed/Idea 131 of 218
Ideas this builds on
Advanced DP patterns131 / 218

Longest Increasing Subsequence faster

Picture

For values 3,5,2, a length-one subsequence ending at 2 is more useful than one ending at 3.

Predict

Does the tails array always equal an actual subsequence?

Walk through the small example

No. For each achievable length, only the smallest ending value matters for future extensions.

Mental model and complete Python solution

Many subsequences of equal length compete. Keep only the one with the smallest tail because it is never harder to extend. Tails across lengths are ordered, enabling binary search.

What the function promises: Return the length of a strictly increasing subsequence. Empty input returns zero.

def lis_length(values):
    tails = []
    for value in values:
        left, right = 0, len(tails)
        while left < right:
            middle = (left+right)//2
            if tails[middle] >= value: right = middle
            else: left = middle+1
        if left == len(tails): tails.append(value)
        else: tails[left] = value
    return len(tails)

assert lis_length([10,9,2,5,3,7,101,18]) == 4
assert lis_length([2,2,2]) == 1

Work and storage: O(n log n) time and O(n) space.

When to recognize it: For each achievable length, only the smallest ending value matters for future extensions.

Check before applying it: Using upper bound instead of lower bound changes strict increasing into nondecreasing behavior.

Change one rule

Return one actual LIS.

See what changes

Store predecessor indices and which input index currently realizes each tails length.

Connect

subsequences: Compress the quadratic predecessor scan into best tails.

Binary search: find a boundary: Find the first tail that cannot stay before the new value.