One coordinate does not mean one transition
Picture
A subsequence preserves order but may skip positions. To find a longest increasing subsequence, anchor the question at its final position: “What is the best increasing subsequence that must end here?” That final value tells you which earlier answers can legally connect to it.
Predict
For [3, 1, 2], can a contiguous-window method find every candidate for the longest increasing subsequence in general? What distinction should you check first?
Reveal the reasoning
Check whether skipping is allowed. A window represents contiguous positions; a subsequence may bridge gaps. For example, [1, 9, 2, 3] has increasing subsequence [1, 2, 3] across a skipped 9.
Go deeper: the endpoint makes compatibility visible
Let dp[i] be the length of the longest strictly increasing subsequence ending at i. A single element gives length 1. Any earlier j with values[j] < values[i] can be its predecessor.
def lis_length(values):
dp = [1] * len(values)
for i in range(len(values)):
for j in range(i):
if values[j] < values[i]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp, default=0)
assert lis_length([1, 9, 2, 3]) == 3
assert lis_length([2, 2, 2]) == 1
assert lis_length([]) == 0
On [1, 9, 2, 3], the table becomes [1, 2, 2, 3]. The final answer is the maximum over endpoints; it need not end at the array’s final position.
There are n states but up to n predecessors each: O(n²) time and O(n) space. Draw an edge j → i when j<i and values[j]<values[i]. This is a DAG because indices increase, and the recurrence finds a longest path length in that implicit graph.
An O(n log n) alternative stores the smallest possible tail value for each subsequence length and uses a binary-search boundary. That requires a new invariant; memorizing it without the state-based baseline hides why replacement is safe. Keep it as a follow-up derivation after this card.
Analogy limit: dp[i] must mean “ending at i,” not simply “best in the first i positions.” Those contracts permit different transitions because only the former guarantees the endpoint’s value.
Change one rule
The subsequence may be nondecreasing instead of strictly increasing. What comparison changes, and what is the answer for [2, 2, 2]?
Check the variation
Use values[j] <= values[i]. The result becomes 3. If using the faster tail-array method, its binary-search boundary must also change to handle equal values correctly.
Connect
Two-string DP tracks progress through two sequences. Invariants explain the faster method’s boundary. Cost separates number of states from work per state.