Edit Distance — 2D DP on a grid
Edit Distance — 2D DP on a grid
Turn "horse" into "ros" using the fewest single-character edits — insert, delete, or substitute. Answer: 3. Levenshtein distance. Spell-checkers, DNA alignment, diff tools all descend from it.
The state
Let dp[i][j] = edit distance between first i chars of word1 and first j chars of word2. Answer is dp[m][n].
Grid is (m+1) × (n+1) — row 0 and column 0 represent empty prefixes.
Base cases
dp[i][0] = i— delete all i chars.dp[0][j] = j— insert all j chars.
The transition
Compare word1[i-1] and word2[j-1]:
- Match:
dp[i][j] = dp[i-1][j-1]. Free — inherit diagonal. - Differ: pay 1 + min of three moves — delete, insert, substitute.
Each cell depends on top, left, top-left. Fill row by row, left to right.
Reading the operations
Walk backward from dp[m][n] to dp[0][0], checking which neighbor produced each value. Same trick used in git's diff.
Common pitfalls
- Off-by-one indexing.
dp[i][j]comparesword1[i-1]toword2[j-1]. - Forgetting the match case is free, not 1.
- Naive recursion without memoization is exponential.
Space optimization
Each row depends only on the previous row. Collapse to two 1D arrays for O(min(m, n)) space.
Try it
- Modify
edit_distanceto also return the sequence of operations by backtracking after the DP fill. - Solve Longest Common Subsequence with the same 2D template: match →
dp[i-1][j-1] + 1, mismatch →max(dp[i-1][j], dp[i][j-1]).
3 graded exercises
Write the code, click Check. We’ll tell you exactly what to fix.
- Exercise 1
Write
min_edits(a, b)that returns the minimum number of single-character inserts, deletes, or replaces needed to turn stringainto stringb. Use DP. - Exercise 2
Write
one_edit_away(a, b)that returns True ifaandbdiffer by at most one insert, delete, or replace. Do it in linear time, no full DP table. - Exercise 3
Write
lcs_length(a, b)returning the length of the longest common subsequence (characters in the same order, not necessarily contiguous).