Edit Distance
Given two strings word1 and word2, return the minimum number of single-character operations required to convert word1 into word2. You may insert, delete, or replace a character. When the current characters match, no operation is needed — carry forward the answer for both strings one character shorter, a smaller subproblem already solved. When they don't match, an operation is unavoidable: try a replace, an insert, and a delete, each pointing at one of three neighboring subproblems , and keep the cheapest.
Constraints
- 0 ≤ word1.length, word2.length ≤ 500
- word1 and word2 consist of lowercase English letters
Example
word1 = "horse", word2 = "ros"3Explanation horse -> rorse (replace h with r) -> rose (delete r) -> ros (delete e), 3 operations.
Fill one row per character, matching or taking the cheapest edit
word1="ab", word2="ac". Turning empty into "", "a", "ac" costs 0, 1, 2 inserts: row = [0, 1, 2].
What happens in this step
word1 = "ab", word2 = "ac" dp[0][0] = 0, dp[0][1] = 1, dp[0][2] = 2 Turning the empty string into a prefix of word2 of length j always takes j inserts — this row is the base case.
Steps to visualize
- Build a table with an extra empty row and column: dpi[0] = i and dp0[j] = j, since turning an empty string into a string of length k always takes k inserts.
- When word1[i-1] equals word2[j-1], dpi[j] = dp[i-1][j-1] — no edit needed.
- Otherwise, dpi[j] = 1 + the smallest of the replace, insert, and delete subproblems.
- The bottom-right cell holds the minimum number of operations.
Walk through the code
Same walkthrough, now with the code. Press Next to move one step and watch which lines run.
word1="ab", word2="ac". Turning empty into "", "a", "ac" costs 0, 1, 2 inserts: row = [0, 1, 2].
What happens in this step
word1 = "ab", word2 = "ac" dp[0][0] = 0, dp[0][1] = 1, dp[0][2] = 2 Turning the empty string into a prefix of word2 of length j always takes j inserts — this row is the base case.
Solution
function minDistance(word1, word2) {
const m = word1.length;
const n = word2.length;
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
for (let i = 0; i <= m; i++) dp[i][0] = i;
for (let j = 0; j <= n; j++) dp[0][j] = j;
for (let i = 1; i <= m; i++) {
for (let 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 + Math.min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}- Time
- O(m * n)
- Space
- O(m * n)
Test cases
| Input | Expected | Covers |
|---|---|---|
word1 = "horse", word2 = "ros" | 3 | example from the docstring |
word1 = "intention", word2 = "execution" | 5 | a longer classic worked example |
word1 = "abc", word2 = "abc" | 0 | identical strings need no edits |
word1 = "", word2 = "abc" | 3 | building a string entirely from inserts |
word1 = "abc", word2 = "" | 3 | reducing a string entirely with deletes |
word1 = "", word2 = "" | 0 | smallest valid input, both strings empty |
word1 = "a", word2 = "b" | 1 | a single replace operation |
word1 = "a", word2 = "ab" | 1 | a single insert operation |