Edit Distance
Difficulty: Hard
You're given two words. Find the minimum number of single-character edits needed to turn the first word into the second one. The allowed edits are: insert a character, delete a character, or replace one character with another.
Examples
Input: word1 = "horse", word2 = "ros"
Output: 3
horse -> rorse (replace 'h' with 'r') -> rose (delete 'r') -> ros (delete 'e').
Input: word1 = "intention", word2 = "execution"
Output: 5
Constraints
0 <= word1.length, word2.length <= 500
Both words contain only lowercase English letters
Approach
Build a table where cell (i, j) means: "the minimum number of edits to turn the first i characters of word1 into the first j characters of word2."
If those two characters already match, this position costs nothing — the answer is exactly the answer for one character back in both words, (i-1, j-1). If they don't match, one edit must happen here, and it's worth 1 plus the cheapest of the three options: delete word1's character ((i-1, j)), insert a character to match word2's ((i, j-1)), or replace word1's character with word2's ((i-1, j-1)).
The base cases handle turning a string into an empty one (or vice versa): turning the first i characters of word1 into nothing takes i deletions, and turning nothing into the first j characters of word2 takes j insertions.
Solutions
Brute Force — Recursion
Walk both words from the front. If the current characters match, move both forward for free. Otherwise, try all three edit types and recurse, taking whichever leads to fewer total edits.
function minDistance(word1, word2) {
function solve(i, j) {
if (i === word1.length) return word2.length - j;
if (j === word2.length) return word1.length - i;
if (word1[i] === word2[j]) return solve(i + 1, j + 1);
const insert = 1 + solve(i, j + 1);
const remove = 1 + solve(i + 1, j);
const replace = 1 + solve(i + 1, j + 1);
return Math.min(insert, remove, replace);
}
return solve(0, 0);
}Time: O(3^(m + n)) in the worst case · Space: O(m + n) — recursion depth
Optimal — Bottom-Up 2D Table
Build a table over (prefix length of word1, prefix length of word2). Seed row 0 and column 0 with the all-insert / all-delete base cases, then fill the rest using the match/no-match rule.
function minDistance(word1, word2) {
const m = word1.length;
const n = word2.length;
const table = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
for (let i = 0; i <= m; i++) table[i][0] = i; // delete all i characters of word1
for (let j = 0; j <= n; j++) table[0][j] = j; // insert all j characters of word2
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (word1[i - 1] === word2[j - 1]) {
table[i][j] = table[i - 1][j - 1];
} else {
table[i][j] = 1 + Math.min(
table[i - 1][j], // delete
table[i][j - 1], // insert
table[i - 1][j - 1] // replace
);
}
}
}
return table[m][n];
}Time: O(m * n) · Space: O(m * n)