StringProblem 14 of 43
EDIT Distance [Very Imp]
Problem Statement
Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2. The allowed operations are:
- Insert a character
- Delete a character
- Replace a character
This is also known as the Levenshtein Distance.
Example:
-
Input:
word1 = "horse",word2 = "ros" -
Output:
3 -
Explanation:
- horse → rorse (replace 'h' with 'r')
- rorse → rose (remove 'r')
- rose → ros (remove 'e')
-
Input:
word1 = "intention",word2 = "execution" -
Output:
5
Approach 1: Brute Force (Recursion)
Intuition
At each step, compare characters from both strings. If they match, move to next characters. If not, try all three operations and take the minimum.
Algorithm
- If either string is empty, return length of the other
- If characters match, no operation needed, move both pointers
- If they don't match, try insert, delete, replace and take minimum
java
public class Solution {
public int minDistance(String word1, String word2) {
return editDistance(word1, word2, 0, 0);
}
private int editDistance(String word1, String word2, int i, int j) {
// Base cases
if (i == word1.length()) {
return word2.length() - j;
}
if (j == word2.length()) {
return word1.length() - i;
}
// If characters match
if (word1.charAt(i) == word2.charAt(j)) {
return editDistance(word1, word2, i + 1, j + 1);
}
// Try all three operations
int insertOp = editDistance(word1, word2, i, j + 1);
int deleteOp = editDistance(word1, word2, i + 1, j);
int replaceOp = editDistance(word1, word2, i + 1, j + 1);
return 1 + Math.min(insertOp, Math.min(deleteOp, replaceOp));
}
}Complexity Analysis
- Time Complexity: O(3^(m+n)) - Three choices at each step
- Space Complexity: O(m+n) - Recursion stack
Approach 2: Memoization (Top-Down DP)
Intuition
Many subproblems are computed multiple times. Use a 2D table to store results.
java
import java.util.*;
public class Solution {
private int[][] memo;
public int minDistance(String word1, String word2) {
memo = new int[word1.length()][word2.length()];
for (int[] row : memo) {
Arrays.fill(row, -1);
}
return editDistance(word1, word2, 0, 0);
}
private int editDistance(String word1, String word2, int i, int j) {
if (i == word1.length()) {
return word2.length() - j;
}
if (j == word2.length()) {
return word1.length() - i;
}
if (memo[i][j] != -1) {
return memo[i][j];
}
if (word1.charAt(i) == word2.charAt(j)) {
memo[i][j] = editDistance(word1, word2, i + 1, j + 1);
} else {
int insertOp = editDistance(word1, word2, i, j + 1);
int deleteOp = editDistance(word1, word2, i + 1, j);
int replaceOp = editDistance(word1, word2, i + 1, j + 1);
memo[i][j] = 1 + Math.min(insertOp, Math.min(deleteOp, replaceOp));
}
return memo[i][j];
}
}Complexity Analysis
- Time Complexity: O(m × n)
- Space Complexity: O(m × n)
Approach 3: Tabulation (Bottom-Up DP)
Intuition
Build the solution bottom-up. dp[i][j] represents the minimum operations to convert word1[0..i-1] to word2[0..j-1].
Algorithm
dp[i][0] = i(delete all characters)dp[0][j] = j(insert all characters)- If characters match:
dp[i][j] = dp[i-1][j-1] - Otherwise:
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
java
public class Solution {
public int minDistance(String word1, String word2) {
int m = word1.length();
int n = word2.length();
// dp[i][j] = min operations to convert word1[0..i-1] to word2[0..j-1]
int[][] dp = new int[m + 1][n + 1];
// Base cases
for (int i = 0; i <= m; i++) {
dp[i][0] = i;
}
for (int j = 0; j <= n; j++) {
dp[0][j] = j;
}
// Fill the table
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];
}
}Complexity Analysis
- Time Complexity: O(m × n)
- Space Complexity: O(m × n)
Approach 4: Space Optimized DP
Intuition
Since we only need the previous row to compute the current row, we can reduce space to O(n).
java
public class Solution {
public int minDistance(String word1, String word2) {
int m = word1.length();
int n = word2.length();
int[] prev = new int[n + 1];
int[] curr = new int[n + 1];
// Base case: first row
for (int j = 0; j <= n; j++) {
prev[j] = j;
}
for (int i = 1; i <= m; i++) {
curr[0] = i;
for (int j = 1; j <= n; j++) {
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
curr[j] = prev[j - 1];
} else {
curr[j] = 1 + Math.min(prev[j],
Math.min(curr[j - 1], prev[j - 1]));
}
}
int[] temp = prev;
prev = curr;
curr = temp;
}
return prev[n];
}
}Complexity Analysis
- Time Complexity: O(m × n)
- Space Complexity: O(n)
Reconstructing the Operations
Key Takeaways
- Edit Distance is a fundamental string similarity measure
- The three operations map directly to DP transitions:
- Delete →
dp[i-1][j] - Insert →
dp[i][j-1] - Replace →
dp[i-1][j-1]
- Delete →
- Bottom-up DP is preferred for its iterative nature
- Space can be optimized from O(m×n) to O(min(m,n))
- Used in spell checkers, DNA sequence alignment, plagiarism detection
- This is one of the most important DP problems to master