Skip to content
JZLeetCode
Go back

LeetCode 72 Edit Distance

Table of contents

Open Table of contents

Description

Question Links: LeetCode 72

Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2.

You have the following three operations permitted on a word:

Example 1:

Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation:
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')

Example 2:

Input: word1 = "intention", word2 = "execution"
Output: 5
Explanation:
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')

Constraints:

0 <= word1.length, word2.length <= 500
word1 and word2 consist of lowercase English letters.

Solution 1: 1D DP (Space-Optimized)

Idea

Define dp[j] as the edit distance from word1[0..i] to word2[0..j]. Since each cell only depends on the current row and the previous row, we can compress the 2D table into a 1D array of size n+1, keeping a prev variable for the diagonal.

word1 = "horse", word2 = "ros"
let m = 5, n = 3

Initialize dp = [0, 1, 2, 3]  (base: converting "" to word2[0..j])

i=1 (h):  dp = [1, 1, 2, 3]
i=2 (o):  dp = [2, 2, 1, 2]
i=3 (r):  dp = [3, 2, 2, 2]
i=4 (s):  dp = [4, 3, 3, 2]
i=5 (e):  dp = [5, 4, 4, 3]  -> answer: dp[3] = 3

Complexity: Time O(mn)O(mn) — two nested loops over m rows and n columns. Space O(n)O(n) for the 1D dp array.

Java

public static int minDistance(String word1, String word2) {
    int m = word1.length(), n = word2.length();
    int[] dp = new int[n + 1];
    for (int j = 0; j <= n; j++) dp[j] = j; // base case: empty word1
    for (int i = 1; i <= m; i++) { // O(m) rows
        int prev = dp[0]; // diagonal from previous row
        dp[0] = i;
        for (int j = 1; j <= n; j++) { // O(n) columns
            int temp = dp[j];
            if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                dp[j] = prev;
            } else {
                dp[j] = 1 + Math.min(prev, Math.min(dp[j], dp[j - 1]));
                // prev=replace, dp[j]=delete, dp[j-1]=insert
            }
            prev = temp;
        }
    }
    return dp[n];
}

Python

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m, n = len(word1), len(word2)
        dp = list(range(n + 1))  # O(n) space: base case dp[j] = j
        for i in range(1, m + 1):  # O(m) rows
            prev = dp[0]
            dp[0] = i
            for j in range(1, n + 1):  # O(n) cols
                temp = dp[j]
                if word1[i - 1] == word2[j - 1]:
                    dp[j] = prev
                else:
                    dp[j] = 1 + min(prev, dp[j], dp[j - 1])  # replace, delete, insert
                prev = temp
        return dp[n]

C++

int minDistance(string word1, string word2) {
    int m = word1.size(), n = word2.size();
    vector<int> dp(n + 1);
    for (int j = 0; j <= n; j++) dp[j] = j;
    for (int i = 1; i <= m; i++) {
        int prev = dp[0];
        dp[0] = i;
        for (int j = 1; j <= n; j++) {
            int temp = dp[j];
            if (word1[i - 1] == word2[j - 1]) {
                dp[j] = prev;
            } else {
                dp[j] = 1 + min({prev, dp[j], dp[j - 1]});
            }
            prev = temp;
        }
    }
    return dp[n];
}

Rust

pub fn min_distance(word1: String, word2: String) -> i32 {
    let a: Vec<char> = word1.chars().collect();
    let b: Vec<char> = word2.chars().collect();
    let (m, n) = (a.len(), b.len());
    let mut dp = (0..=n as i32).collect::<Vec<i32>>();
    for i in 1..=m {
        let mut prev = dp[0];
        dp[0] = i as i32;
        for j in 1..=n {
            let tmp = dp[j];
            if a[i - 1] == b[j - 1] {
                dp[j] = prev;
            } else {
                dp[j] = 1 + prev.min(dp[j]).min(dp[j - 1]);
            }
            prev = tmp;
        }
    }
    dp[n]
}

Solution 2: 2D DP

Idea

Use a full 2D table dp[i][j] representing the edit distance from word1[0..i] to word2[0..j]. Base cases: dp[i][0] = i (delete all) and dp[0][j] = j (insert all). For each cell, if characters match, carry forward the diagonal; otherwise take the minimum of replace, delete, or insert plus one.

         ""  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 ]

dp[5][3] = 3

Complexity: Time O(mn)O(mn) — two nested loops. Space O(mn)O(mn) for the 2D dp table.

Java

public static int minDistance2D(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; // base case: empty word2
    for (int j = 0; j <= n; j++) dp[0][j] = j; // base case: empty word1
    for (int i = 1; i <= m; i++) { // O(m) rows
        for (int j = 1; j <= n; j++) { // O(n) columns
            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 - 1], Math.min(dp[i - 1][j], dp[i][j - 1]));
                // dp[i-1][j-1]=replace, dp[i-1][j]=delete, dp[i][j-1]=insert
            }
        }
    }
    return dp[m][n];
}

Python

class Solution2:
    def minDistance(self, word1: str, word2: str) -> int:
        m, n = len(word1), len(word2)
        dp = [[0] * (n + 1) for _ in range(m + 1)]  # O(mn) space
        for i in range(m + 1):
            dp[i][0] = i
        for j in range(n + 1):
            dp[0][j] = j
        for i in range(1, m + 1):  # O(m) rows
            for j in range(1, n + 1):  # O(n) cols
                if word1[i - 1] == word2[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1]
                else:
                    dp[i][j] = 1 + min(
                        dp[i - 1][j - 1],  # replace
                        dp[i - 1][j],  # delete
                        dp[i][j - 1],  # insert
                    )
        return dp[m][n]

C++

int minDistance(string word1, string word2) {
    int m = word1.size(), n = word2.size();
    vector<vector<int>> dp(m + 1, vector<int>(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[i - 1] == word2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = 1 + min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]});
            }
        }
    }
    return dp[m][n];
}

Rust

pub fn min_distance_2d(word1: String, word2: String) -> i32 {
    let a: Vec<char> = word1.chars().collect();
    let b: Vec<char> = word2.chars().collect();
    let (m, n) = (a.len(), b.len());
    let mut dp = vec![vec![0i32; n + 1]; m + 1];
    for i in 0..=m {
        dp[i][0] = i as i32;
    }
    for j in 0..=n {
        dp[0][j] = j as i32;
    }
    for i in 1..=m {
        for j in 1..=n {
            if a[i - 1] == b[j - 1] {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = 1 + dp[i - 1][j - 1].min(dp[i - 1][j]).min(dp[i][j - 1]);
            }
        }
    }
    dp[m][n]
}
Share this post on:

Previous Post
System Design - How Linux Containers Work Under the Hood
Next Post
LeetCode 128 Longest Consecutive Sequence