Intuition The one core idea
Two strings share a "skeleton" of letters that appear in both, in the same left-to-right order but with gaps allowed — that skeleton is the Longest Common Subsequence . Everything else on the parent page is just bookkeeping: we chop both strings into prefixes , name each prefix-vs-prefix answer with a two-number label L ( i , j ) , and fill a grid so we never recompute the same sub-question twice.
This page assumes you have seen nothing . We build every letter, arrow, and bracket the parent LCS note leans on, one brick at a time. If a term below already feels obvious, skim — but each one is a place beginners silently trip.
A string is just an ordered list of characters (letters/symbols), read left to right. We write it in code font: ABCBDAB.
"Ordered" means position matters: AB is not the same string as BA.
The length of a string is how many characters it holds. ABCBDAB has length 7.
The picture: think of beads on a straight wire. Each bead is a character, and the wire fixes their order — you cannot slide bead 4 in front of bead 2 without cutting the wire.
Why the topic needs it. LCS compares two such wires and asks which beads line up in both, keeping the wire order intact. Without a fixed order there would be no "same relative order" to protect.
The parent writes X = x 1 x 2 … x m and Y = y 1 y 2 … y n . Let's earn every piece.
X , Y , x i , y j , m , n
X and Y are names for the two whole strings we compare. Using capital letters means "the entire string".
x i (little x, subscript i ) means "the ==character at position i inside X =="." So if X = AGGTAB, then x 1 = A, x 2 = G, x 4 = T.
y j is the same idea for Y : the character at position j .
m is the length of X ; n is the length of Y . So positions run x 1 … x m and y 1 … y n .
Intuition Why a subscript instead of a new letter each time?
A subscript is a pointer : instead of inventing new names first, second, third, we say "give me character number i " and let i be any position. This is what lets us write ONE rule that works for every position at once. i and j are just two independent pointers — one walking along X , one walking along Y .
The picture: two rows of beads stacked. A finger labelled i points at one bead in the top row X ; a finger labelled j points at one bead in the bottom row Y .
x i as "x times i"
Why it feels right: x i looks like algebra multiplication.
Fix: there is no multiplication here. The subscript is an address , like "house number i on street X ". x i is a single character, never a product.
This is THE distinction the whole topic rides on.
Definition Subsequence and substring
A subsequence is what remains after deleting some characters (maybe none) without reordering the rest. Gaps are allowed.
A substring is a contiguous block — no gaps, you slice out a run with no holes.
The picture — same string, two different rules:
From ABCDE:
ACE is a subsequence (keep A, skip B, keep C, skip D, keep E — order preserved, gaps allowed).
ACE is not a substring (A, C, E are not next to each other).
BCD is a substring (a solid slice).
Intuition Why "same order but gaps allowed" is the heart of LCS
The letters you keep must appear left-to-right in the same sequence as the original — you may skip , but you may never reshuffle . This one freedom (skip) plus one restriction (no reorder) is exactly what makes LCS harder than "find equal letters" and easier than "find equal blocks".
Why the topic needs it. LCS = longest subsequence common to both strings . Confusing it with substring gives a completely different recurrence (see the parent's mistake box). Nail this now and half the traps vanish. The contiguous cousin lives in Longest Common Substring .
Definition Prefix and the notation
X [ 1.. i ]
A prefix is a starting chunk of a string: the first few characters, from the left, with nothing skipped.
X [ 1.. i ] means "the prefix of X from position 1 up to and including position i ".
X [ 1..0 ] = the empty prefix (zero characters, written "").
X [ 1..3 ] of AGGTAB = AGG.
X [ 1.. m ] = the whole string X .
The picture: a sliding cut. Put a knife after position i ; everything to the left of the knife is the prefix X [ 1.. i ] . Slide the knife right and the prefix grows.
Intuition Why prefixes and not arbitrary chunks?
LCS is solved by shrinking the problem. If we can answer "LCS of the first i of X and first j of Y " for small i , j , we can build up to the full strings. Prefixes are the natural shrinking unit because we always chop from the right end (look at the last character, then step back). That single design choice is why the recurrence exists at all.
L ( i , j )
L ( i , j ) = length of the LCS of the prefixes X [ 1.. i ] and Y [ 1.. j ] .
Read it aloud: "L of i and j is the length of the longest common subsequence when we only look at the first i letters of X and the first j letters of Y ."
Intuition What kind of object is
L ?
L is a function : you feed it two numbers ( i , j ) and it hands back one number — a length. Think of it as a lookup box with two dials i and j ; every dial setting reveals a stored answer. Because there are only ( m + 1 ) choices for i and ( n + 1 ) for j , there are finitely many boxes — which is exactly why we can store them all in a grid.
Why "length" and not the string itself? Lengths are single numbers, trivial to compare and add. We compute all lengths first (fast), then walk backwards to recover an actual string only if we want one (the parent's traceback). Separating "how long" from "which letters" keeps the core simple.
The final answer we want is L ( m , n ) — the box with both dials turned all the way up, i.e. both full strings.
The parent's formula uses two operations. Both deserve a plain-words picture.
max ( a , b )
max means "take the bigger of the two numbers "." max ( 3 , 5 ) = 5 , max ( 2 , 2 ) = 2 .
max and not + on a mismatch?
When the last two characters differ , they cannot both cap the common subsequence. We must drop one character and try again — either drop X 's last (giving L ( i − 1 , j ) ) or drop Y 's last (giving L ( i , j − 1 ) ). We don't know which is better in advance, so we compute both and keep the winner . max is precisely the tool for "try both options, keep the best" — that is what optimisation over choices means . Adding them would double-count; picking the max never over-counts.
+ 1 only on a match?
When the last characters are equal (x i = y j ), we have found one genuine shared letter that can sit at the end of the common subsequence. We reward it with exactly + 1 , then recurse on the smaller prefixes X [ 1.. i − 1 ] and Y [ 1.. j − 1 ] — because that one match "used up" one character from each string. That "one from each" is why we step back on both dials at once (the diagonal). A mismatch supplies no new shared letter, so it earns + 0 .
Definition The two cases in words
Match (x i = y j ): L ( i , j ) = 1 + L ( i − 1 , j − 1 ) — reward and step both dials back.
Mismatch (x i = y j ): L ( i , j ) = max ( L ( i − 1 , j ) , L ( i , j − 1 ) ) — drop one, keep the better.
Here = means "not equal to". The symbol = inside a case is a test ("are these two characters the same?"), not an assignment.
L ( 0 , j ) = L ( i , 0 ) = 0
If either prefix is empty (zero characters), there is nothing to share, so the LCS length is 0 .
The picture: an empty wire has no beads; no bead can match against nothing . So every cell in row 0 or column 0 of the grid is 0 — these are the walls we build outward from.
( m + 1 ) × ( n + 1 ) and not m × n ?
We need real cells to hold those "empty prefix" zeros. Position 0 = "before the string starts". So the grid gets one extra row (for i = 0 ) and one extra column (for j = 0 ), full of zeros. Every later cell can then safely look "up", "left", and "up-left" without falling off the edge. This is also the source of the classic off-by-one : in code, cell L [ i ] [ j ] compares X [ i − 1 ] with Y [ j − 1 ] because code indexes from 0 but our prefixes count from 1 . (See the parent's off-by-one mistake box.)
Every filled cell L ( i , j ) is decided by three neighbours already computed :
Definition The three neighbours
up = L ( i − 1 , j ) — one fewer character of X .
left = L ( i , j − 1 ) — one fewer character of Y .
diagonal (up-left) = L ( i − 1 , j − 1 ) — one fewer of both .
The picture: stand on a cell. The match rule reaches to the diagonal (both dials down). The mismatch rule reaches up and left and keeps the bigger. This directional habit — "match goes diagonal, miss takes the max" — is the entire mechanical skill.
Recall Which neighbour for which case?
Match uses which neighbour? ::: The diagonal (up-left), plus 1.
Mismatch uses which neighbours? ::: Up and left, take the max.
String = ordered characters
Index x_i, y_j pointers into strings
Subsequence keeps order allows gaps
Function L i,j returns a length
max operation keep the bigger
Empty prefix means length 0
LCS recurrence match diagonal miss max
Cover the right side and test yourself. If any answer is fuzzy, reread that section before tackling the parent's derivation.
What does x i mean, and why is it NOT multiplication? The character at position i inside string X ; the subscript is an address/pointer, not a product.
What are m and n ? The lengths (number of characters) of X and Y respectively.
Difference between subsequence and substring? Subsequence keeps order but allows gaps (skips); substring must be contiguous (no gaps).
What is the prefix X [ 1.. i ] ? The starting chunk of X , its first i characters with nothing skipped.
What is X [ 1..0 ] ? The empty prefix "" — zero characters.
In plain words, what is L ( i , j ) ? The length of the longest common subsequence of the first i characters of X and the first j characters of Y .
What kind of object is L ? A function (a two-dial lookup box) taking two numbers and returning one length.
What does max ( a , b ) do and why use it on a mismatch? Returns the bigger of a , b ; we try both "drop one character" options and keep the better one.
Why does a match add exactly + 1 and step back on BOTH dials? One shared letter is found for the end; it uses one character from each string, so both prefixes shrink by one (the diagonal).
Why is the base case 0 ? An empty prefix has no characters, so nothing can be shared.
Why is the grid ( m + 1 ) × ( n + 1 ) instead of m × n ? To hold the row/column of "empty prefix" zeros at index 0, giving every cell safe up/left/diagonal neighbours.
Which cell holds the final answer? The bottom-right corner L ( m , n ) , using both full strings.