3.7.12Algorithm Paradigms

DP problems — edit distance (Levenshtein)

2,014 words9 min readdifficulty · medium

WHAT is it?

We define a 2D table:

dp[i][j]=edit distance between the first i chars of A and the first j chars of Bdp[i][j] = \text{edit distance between the first } i \text{ chars of } A \text{ and the first } j \text{ chars of } B

So dp[m][n]dp[m][n] is our final answer, and dp[0][0]=0dp[0][0]=0 (two empty strings cost nothing).


WHY does a recurrence even exist?

The last move is one of:

  1. Characters match (A[i-1] == B[j-1]): no edit needed for this position → cost passes through from dp[i1][j1]dp[i-1][j-1].
  2. Substitute A[i-1]B[j-1]: cost 1+1 + the cost of aligning the smaller prefixes dp[i1][j1]dp[i-1][j-1].
  3. Delete A[i-1] (it's an extra char in A): cost 1+dp[i1][j]1 + dp[i-1][j].
  4. Insert B[j-1] (it's needed in B): cost 1+dp[i][j1]1 + dp[i][j-1].

We take the minimum because we want the cheapest way.


HOW to derive the recurrence from scratch

Why this is correct: every conversion ends with exactly one of the four cases above, each reduces to a strictly smaller subproblem, and we minimise — so we never miss the optimum (this is optimal substructure + overlapping subproblems).

Figure — DP problems — edit distance (Levenshtein)

Complexity (the 80/20 takeaway)

  • Time: O(mn)O(m \cdot n) — we fill each cell once in O(1)O(1).
  • Space: O(mn)O(m \cdot n) for the full table, or O(min(m,n))O(\min(m,n)) if you only keep the previous row (you only ever read row i1i-1 and column j1j-1).

Worked Example 1 — "horse""ros" (answer = 3)

We fill the table row by row. Let A=A=horse (rows), B=B=ros (cols).

"" r o s
0 1 2 3
h 1 1 1 2 3
o 2 2 2 1 2
r 3 3 2 2 2
s 4 4 3 3 2
e 5 5 4 4 3
  • Why dp[2][2]=1dp[2][2]=1? A[1]='o', B[1]='o' match, so we copy the diagonal dp[1][1]=1dp[1][1]=1. Why this step? Matching costs nothing, so the cost equals fixing the smaller prefixes "h""r".
  • Why dp[5][3]=3dp[5][3]=3? 'e' ≠ 's', so 1+min(dp[4][2],dp[4][3],dp[5][2])=1+min(3,2,4)=31+\min(dp[4][2],dp[4][3],dp[5][2]) = 1+\min(3,2,4)=3. Why this step? Cheapest predecessor is the delete path (delete e), giving the final 3.
  • Reading edits back: substitute h→r? No — actual optimal path is delete h, delete r, substitute e→s (3 edits), matching dp[5][3]=3dp[5][3]=3.

Worked Example 2 — "sunday""saturday" (answer = 3)

Key cells:

  • The shared s...n...day structure means many matches pass straight down the diagonal.
  • We need to insert a and t, and substitute n→r. That's 2+1=32 + 1 = 3.

Why this is 3 and not more? Because the long common subsequence s..day aligns for free; only 3 positions actually differ in a way that needs work. Why this step matters: edit distance secretly rewards long shared subsequences.


Worked Example 3 — Forecast-then-Verify


Common Mistakes


Recall Feynman: explain to a 12-year-old

Imagine you have two words written on Lego strips and you want to make the first word look exactly like the second. You're allowed three moves: stick on a new letter brick, pull off a letter brick, or swap one brick for another — each move costs one coin. Edit distance is the fewest coins you can spend. The clever trick: instead of solving the whole word at once, you make a little grid and figure out the cheapest fix for the tiny beginnings first (just one letter, then two...), writing each answer in a box. Each new box just peeks at three boxes you already filled (up, left, and up-left) and picks the cheapest one. By the time you reach the bottom-right box, the answer is sitting right there!


Active Recall

What does dp[i][j]dp[i][j] represent in edit distance?
The minimum edit distance between the first ii chars of AA and the first jj chars of BB.
What are the three allowed operations and their costs?
Insert, delete, substitute — each costs 1.
What is dp[0][j]dp[0][j] and why?
jj — you must insert jj characters to build B[0..j) from an empty string.
What is dp[i][0]dp[i][0] and why?
ii — you must delete ii characters to reduce A[0..i) to empty.
When A[i-1] == B[j-1], what is dp[i][j]dp[i][j]?
dp[i1][j1]dp[i-1][j-1] (the diagonal, with NO +1).
When characters differ, write the transition.
1+min(dp[i1][j1],dp[i1][j],dp[i][j1])1 + \min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]).
Which neighbour corresponds to a deletion from A?
The cell directly above, dp[i1][j]dp[i-1][j].
Which neighbour corresponds to an insertion into A?
The cell to the left, dp[i][j1]dp[i][j-1].
Time and space complexity?
O(mn)O(mn) time; O(mn)O(mn) space, reducible to O(min(m,n))O(\min(m,n)) keeping one row.
Why does the recurrence only look at the last character?
Any edit sequence must handle the final char of each prefix; fixing it reduces to a smaller solved subproblem (optimal substructure).
Edit distance of "horse" → "ros"?
3.
Is edit distance equal to length difference?
No — that's only a lower bound; substitutions add cost even at equal length.

Connections

  • Dynamic Programming — edit distance is a canonical 2D-table DP.
  • Longest Common Subsequence — closely related; LCS aligns matches, edit distance counts mismatches.
  • Optimal Substructure — the property that makes the recurrence valid.
  • Overlapping Subproblems — why memoisation/tabulation beats naive recursion.
  • Recursion and Memoization — top-down alternative to the bottom-up table.
  • Sequence Alignment — bioinformatics generalisation (Needleman–Wunsch) with weighted costs.
  • Space Optimization in DP — the rolling-row trick to reach O(min(m,n))O(\min(m,n)) space.

Concept Map

uses ops

solved by

fills

answer at

reasons about

case

case

case

case

takes minimum

empty string edits

analysed as

Edit distance A to B

Insert delete substitute
each cost 1

Dynamic Programming

dp table i j on prefixes

Examine last character

Base cases

Match: dp i-1 j-1

Substitute: 1+dp i-1 j-1

Delete: 1+dp i-1 j

Insert: 1+dp i j-1

Recurrence = min of cases

Time O m*n, Space O min m,n

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Edit distance ka matlab simple hai: do strings di hui hain, aur humein batana hai ki pehli string ko doosri banane ke liye kam se kam kitne single-character edits chahiye. Edits sirf teen type ke allowed hain — ek letter insert karo, ek letter delete karo, ya ek letter ko doosre se substitute (replace) karo. Har edit ka cost 1 hai. Jaise "horse" ko "ros" banane mein 3 edits lagte hain.

Ab DP wala jugaad: hum poori string ek saath solve nahi karte. Hum ek grid (table) banate hain jahan dp[i][j] ka matlab hai "A ke pehle i letters ko B ke pehle j letters banane ka minimum cost". Sabse pehle base cases bharo — agar ek string khaali hai toh saare letters insert ya delete karne padenge, isliye dp[0][j]=j aur dp[i][0]=i. Phir har cell teen padosi cells dekhta hai: diagonal (up-left) substitute/match ke liye, upar wala delete ke liye, aur left wala insert ke liye. Agar current letters match karte hain toh diagonal ko bina +1 ke copy kar lo (free hai!), warna 1 + min(teen padosi).

Yeh kyu important hai? Spell-checkers, DNA sequence matching, "did you mean..." search suggestions, aur diff tools — sab isi idea pe chalte hain. Interview mein bhi yeh classic 2D-DP question hai. Time complexity O(mn)O(mn) hai aur thoda smart bano toh space sirf ek row mein ho jaata hai.

Ek common galti yaad rakho: indexing mein gadbad. dp[i][j] mein current characters A[i-1] aur B[j-1] hote hain, A[i] nahi — kyunki i ka matlab "pehle i letters" hai. Aur jab letters match karein tab +1 mat lagana, sirf diagonal copy karna.

Go deeper — visual, from zero

Test yourself — Algorithm Paradigms

Connections