The edit distance between two strings is the minimum number of single-character operations — insert, delete, or replace — to transform one into the other. This lesson covers the classic Levenshtein DP (LeetCode 72), variants, and full solutions in C, Java, and Python with trace tables.
Overview
Given strings word1 and word2, compute the minimum edits to make them equal. This is the foundation of spell-checking, DNA alignment, and fuzzy string matching.
State: dp[i][j] = min edits to turn word1[0..i-1] into word2[0..j-1]
Base: dp[i][0] = i (delete i chars from word1 prefix)
dp[0][j] = j (insert j chars to match word2 prefix)
Match: dp[i][j] = dp[i-1][j-1]
Mismatch: dp[i][j] = 1 + min(
dp[i-1][j], // delete from word1
dp[i][j-1], // insert into word1
dp[i-1][j-1] // replace
)
def min_distance(word1, word2):
"""LeetCode 72 — Levenshtein edit distance. O(m × n)."""
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
# base: transform prefix to / from empty string
for i in range(m + 1):
dp[i][0] = i # i deletions
for j in range(n + 1):
dp[0][j] = j # j insertions
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1] # chars match — no edit
else:
dp[i][j] = 1 + min(
dp[i - 1][j], # delete word1[i-1]
dp[i][j - 1], # insert word2[j-1]
dp[i - 1][j - 1] # replace word1[i-1]
)
return dp[m][n]
if __name__ == "__main__":
tests = [("horse", "ros"), ("intention", "execution"), ("", "a")]
for w1, w2 in tests:
print(f"minDistance('{w1}', '{w2}') = {min_distance(w1, w2)}")
Java
public class EditDistance {
/** Levenshtein distance — classic 2D DP. */
public static int minDistance(String word1, String word2) {
int m = word1.length(), n = word2.length();
int[][] dp = new int[m + 1][n + 1];
for (int i = 0; i <= m; i++) dp[i][0] = i;
for (int j = 0; j <= n; j++) dp[0][j] = j;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1 + Math.min(
dp[i - 1][j],
Math.min(dp[i][j - 1], dp[i - 1][j - 1])
);
}
}
}
return dp[m][n];
}
public static void main(String[] args) {
System.out.println(minDistance("horse", "ros")); // 3
System.out.println(minDistance("intention", "execution")); // 5
System.out.println(minDistance("", "a")); // 1
}
}
C
#include <stdio.h>
#include <string.h>
#define MAX 1001
int min3(int a, int b, int c) {
if (a <= b && a <= c) return a;
return b <= c ? b : c;
}
int edit_distance(char *word1, char *word2) {
int dp[MAX][MAX];
int m = (int)strlen(word1), n = (int)strlen(word2);
int i, j;
for (i = 0; i <= m; i++) dp[i][0] = i;
for (j = 0; j <= n; j++) dp[0][j] = j;
for (i = 1; i <= m; i++) {
for (j = 1; j <= n; j++) {
if (word1[i - 1] == word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1 + min3(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
}
}
}
return dp[m][n];
}
int main(void) {
printf("%d\n", edit_distance("horse", "ros")); /* 3 */
printf("%d\n", edit_distance("intention", "execution")); /* 5 */
printf("%d\n", edit_distance("", "a")); /* 1 */
return 0;
}
Output & Trace Tables
Program output (all three languages)
Language
Input
Output
Explanation
Python
"horse", "ros"
3
horse → rorse → rose → ros
Python
"intention", "execution"
5
Classic textbook example
Python
"", "a"
1
One insert into empty string
Java / C
same tests
3, 5, 1
Identical logic
DP table trace — word1 = "horse", word2 = "ros"
dp[i][j]
""
r
o
s
""
0
1
2
3
h
1
1
2
3
o
2
2
1
2
r
3
2
2
2
s
4
3
3
2
e
5
4
4
3
Classic example — "kitten" → "sitting" (distance 3)
Step
Operation
Result string
Start
—
kitten
1
Replace k → s
sitten
2
Replace e → i
sittin
3
Insert g
sitting
Reading the table: First row/column counts insertions/deletions to reach empty prefix. Bottom-right cell 3 is the minimum edit distance for "horse" → "ros".
The Three Operations Explained
Operation
DP move
Meaning
Example at (i,j)
Delete
dp[i−1][j] + 1
Remove word1[i−1]
Drop extra char from source
Insert
dp[i][j−1] + 1
Add word2[j−1]
Pad source to match target
Replace
dp[i−1][j−1] + 1
Swap mismatched chars
horse: h→r at first position
Match
dp[i−1][j−1]
Chars equal — free
Both have 'r' at (r,r)
Edit Distance Variants
Variant
Change to standard DP
LeetCode
Delete only (two strings)
On mismatch: min(dp[i−1][j], dp[i][j−1]) + 1 — no replace diagonal+1
Only the previous row is needed — reduce to O(n) space:
prev[j] and curr[j] for each row i = 1..m
match: curr[j] = prev[j-1]
mismatch: curr[j] = 1 + min(prev[j], curr[j-1], prev[j-1])
Keep prevRow[j-1] before overwrite when computing curr[j] (replace case)
Reconstruction: To print the actual edit script, keep the full 2D table (or store direction pointers) and backtrack from (m, n).
More Examples
Example 1: Identical strings
word1
word2
Distance
Why
abc
abc
0
Diagonal path — all matches
Example 2: Completely different (same length)
word1
word2
Distance
Why
abc
def
3
Three replaces (or mix of ops)
Example 3: Complexity summary
Approach
Time
Space
Notes
2D DP
O(m × n)
O(m × n)
Standard interview solution
1D rolling
O(m × n)
O(n)
Space optimized
Top-down memo
O(m × n)
O(m × n) + stack
Natural recursive explanation
Naive recursion
O(3^max(m,n))
O(m+n)
Try all ops — too slow
Approach Comparison
Approach
Time
Space
When to use
Bottom-up 2D
O(m × n)
O(m × n)
Whiteboard + backtracking
Bottom-up 1D
O(m × n)
O(n)
Final optimized code
Top-down memo
O(m × n)
O(m × n)
Explain recurrence recursively first
LCS reduction
O(m × n)
O(m × n)
Delete-only variant (583)
Real-Life Applications
Domain
Real scenario
Edit distance role
Notes
Spell check
Suggest closest dictionary word to typo
Rank candidates by Levenshtein distance
Threshold ≤ 2 for short words
Autocorrect
Mobile keyboard fixes "teh" → "the"
Min edits among lexicon entries
Often combined with frequency
Bioinformatics
DNA/protein sequence alignment
Weighted edit ops (gap penalties)
Needleman–Wunsch variant
Plagiarism / dedup
Near-duplicate document detection
Low edit distance → similar text
Token-level or char-level
Search engines
Fuzzy matching for queries
"Levenstein" still finds "Levenshtein"
Production uses optimized indexes
OCR / speech
Compare recognized output to lexicon
Pick min-distance correction
Noisy input cleanup
Version control
Diff two file lines
Edit script between strings
Related to Myers diff
Record linkage
Match names across databases ("Jon" vs "John")
Fuzzy string similarity score
Threshold tuning per domain
Interview Tips
Initialize first row and column — dp[i][0]=i, dp[0][j]=j — before the nested loops.
State dp[i][j] meaning clearly: edits for prefixes, not full strings.
On mismatch, write all three options: delete, insert, replace.
Trace "horse"/"ros" or "kitten"/"sitting" on a small table.
Distinguish from LCS: LCS maximizes match; edit distance minimizes cost to full equality.
Follow-up: O(n) space with one row; backtrack to list operations.
Delete-only (583) = no replace on mismatch; answer ties to LCS length.
State O(m × n) time and space explicitly.
Key Takeaway
Edit distance fills dp[i][j] with min edits between prefixes: free on match, else 1 + min(delete, insert, replace). Base row/column count insertions/deletions from empty string. Same 2D grid shape as LCS — but mismatch uses three-way min, not max of neighbors.