Edit Distance Problem
Problem and examples
The Edit Distance problem asks: Given two strings word1 and word2, what is the minimum number of operations required to convert word1 into word2?
- Allowed operations are:
- Insert a character
- Delete a character
- Replace a character
- Each operation counts as 1 step.
The goal is to find the minimum number of such operations needed to make the two strings equal.
| Word 1 | Word 2 | Edit Distance | Description |
|---|---|---|---|
| "horse" | "ros" | 3 | Replace 'h' → 'r', remove 'r', remove 'e' |
| "intention" | "execution" | 5 | Requires multiple inserts, deletes, and replacements |
| "abc" | "abc" | 0 | Both strings are already equal |
| "abc" | "def" | 3 | All characters need to be replaced |
| "abcd" | "abc" | 1 | Only one deletion is needed |
| "abc" | "abcd" | 1 | Only one insertion is needed |
| "" | "abc" | 3 | Empty word1, need 3 insertions |
| "abc" | "" | 3 | Empty word2, need 3 deletions |
| "" | "" | 0 | Both strings are empty, no operations needed |
Explore Edit Distance Problem - DP
Load an example or enter your own input. Use the player controls to follow each change.
Understanding the Problem
The Edit Distance problem asks us to find the minimum number of operations required to convert one string into another. The allowed operations are:
- Insert a character
- Delete a character
- Replace a character
Each operation costs 1 unit, and our goal is to convert word1 to word2 in the fewest steps possible.
Step-by-Step Solution with Example
Step 1: Understand the goal through an example
Suppose word1 = "horse" and word2 = "ros". We need to transform "horse" into "ros" using the fewest insertions, deletions, or replacements.
Step 2: Break down the transformations
Let's compare characters step by step:
- 'h' in "horse" ≠ 'r' in "ros" → Replace 'h' with 'r' (cost: 1)
- 'o' = 'o' → No operation (cost: 0)
- 'r' = 's'? No → Replace 'r' with 's' or delete 'r' and insert 's'
- Continue comparing and choosing the minimal cost operation at each step
Step 3: Use Dynamic Programming
We define dp[i][j] as the edit distance between the first i characters of word1 and the first j characters of word2.
- If
i == 0, we needjinsertions - If
j == 0, we needideletions - If
word1[i-1] == word2[j-1], thendp[i][j] = dp[i-1][j-1] - Else,
dp[i][j] = 1 + min(dp[i-1][j] // deletedp[i][j-1] // insertdp[i-1][j-1] // replace
)
Step 4: Build the DP table
We start with an empty 2D table of size (m+1) x (n+1) where m = length of word1 and n = length of word2. Fill in the base cases for empty strings, then fill the rest based on the rules above.
Step 5: Get the result
The final answer will be in dp[m][n], which tells us the edit distance for the full lengths of both strings.
Edge Cases
- Both strings are empty: The edit distance is 0
- One string is empty: The edit distance is the length of the other string (all insertions or deletions)
- Both strings are the same: No operations needed; distance is 0
- Case sensitivity: 'A' ≠ 'a'; so replacement is needed if case doesn't match
Finally
This problem teaches a foundational dynamic programming technique. By comparing subproblems and building up the solution in a 2D table, we avoid recomputation and ensure the optimal number of steps.
Always begin by breaking the problem into manageable steps, test your solution with examples, and handle edge cases thoughtfully to ensure correctness.
- Let
word1andword2be the input strings, with lengthsmandnrespectively. - Create a 2D table
dpof dimensions(m+1) x (n+1), wheredp[i][j]represents the edit distance between the firsticharacters ofword1and the firstjcharacters ofword2. - Initialize the first column: for each
ifrom0tom, setdp[i][0] = i(representingideletions). - Initialize the first row: for each
jfrom0ton, setdp[0][j] = j(representingjinsertions). - For each
ifrom1tomand for eachjfrom1ton: - If
word1[i-1]equalsword2[j-1], then setdp[i][j] = dp[i-1][j-1]. - Otherwise, set
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]). - The edit distance is
dp[m][n].
Implementation and efficiency
C implementation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int min(int a, int b, int c) {
int m = a < b ? a : b;
return m < c ? m : c;
}
int editDistance(const char *word1, const char *word2) {
int m = strlen(word1), n = strlen(word2);
int **dp = malloc((m + 1) * sizeof(int*));
for (int i = 0; i <= m; i++) {
dp[i] = malloc((n + 1) * sizeof(int));
}
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], dp[i][j - 1], dp[i - 1][j - 1]);
}
}
}
int distance = dp[m][n];
for (int i = 0; i <= m; i++) free(dp[i]);
free(dp);
return distance;
}
int main() {
printf("Edit Distance between 'kitten' and 'sitting': %d\n", editDistance("kitten", "sitting"));
printf("Edit Distance between 'horse' and 'ros': %d\n", editDistance("horse", "ros"));
return 0;
}C++ implementation
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int editDistance(const string &word1, const string &word2) {
int m = word1.size(), n = word2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
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], dp[i][j - 1], dp[i - 1][j - 1]});
}
}
}
return dp[m][n];
}
int main() {
cout << "Edit Distance between 'kitten' and 'sitting': " << editDistance("kitten", "sitting") << endl;
cout << "Edit Distance between 'horse' and 'ros': " << editDistance("horse", "ros") << endl;
return 0;
}Python implementation
def edit_distance(word1, word2):
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
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):
for j in range(1, n + 1):
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], dp[i][j - 1], dp[i - 1][j - 1])
return dp[m][n]
if __name__ == '__main__':
print("Edit Distance between 'kitten' and 'sitting':", edit_distance("kitten", "sitting"))
print("Edit Distance between 'horse' and 'ros':", edit_distance("horse", "ros"))Java implementation
public class EditDistance {
public static int editDistance(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("Edit Distance between 'kitten' and 'sitting': " + editDistance("kitten", "sitting"));
System.out.println("Edit Distance between 'horse' and 'ros': " + editDistance("horse", "ros"));
}
}JavaScript implementation
function editDistance(word1, word2) {
const m = word1.length, 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], dp[i][j - 1], dp[i - 1][j - 1]);
}
}
}
return dp[m][n];
}
console.log("Edit Distance between 'kitten' and 'sitting':", editDistance("kitten", "sitting"));
console.log("Edit Distance between 'horse' and 'ros':", editDistance("horse", "ros"));Go implementation
package main
import (
"fmt"
)
func min(a, b, c int) int {
if a < b {
if a < c {
return a
}
return c
}
if b < c {
return b
}
return c
}
func editDistance(word1, word2 string) int {
m, n := len(word1), len(word2)
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
}
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 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
}
}
}
return dp[m][n]
}
func main() {
fmt.Println("Edit Distance between 'kitten' and 'sitting':", editDistance("kitten", "sitting"))
fmt.Println("Edit Distance between 'horse' and 'ros':", editDistance("horse", "ros"))
}Rust implementation
fn edit_distance(word1: &str, word2: &str) -> usize {
let m = word1.len();
let n = word2.len();
let w1: Vec<char> = word1.chars().collect();
let w2: Vec<char> = word2.chars().collect();
let mut dp = vec![vec![0; n + 1]; m + 1];
for i in 0..=m { dp[i][0] = i; }
for j in 0..=n { dp[0][j] = j; }
for i in 1..=m {
for j in 1..=n {
if w1[i - 1] == w2[j - 1] {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1 + std::cmp::min(dp[i - 1][j], std::cmp::min(dp[i][j - 1], dp[i - 1][j - 1]));
}
}
}
dp[m][n]
}
fn main() {
println!("Edit Distance between 'kitten' and 'sitting': {}", edit_distance("kitten", "sitting"));
println!("Edit Distance between 'horse' and 'ros': {}", edit_distance("horse", "ros"));
}Kotlin implementation
fun editDistance(word1: String, word2: String): Int {
val m = word1.length
val n = word2.length
val dp = Array(m + 1) { IntArray(n + 1) }
for (i in 0..m) dp[i][0] = i
for (j in 0..n) dp[0][j] = j
for (i in 1..m) {
for (j in 1..n) {
dp[i][j] = if (word1[i - 1] == word2[j - 1]) {
dp[i - 1][j - 1]
} else {
1 + minOf(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
}
}
}
return dp[m][n]
}
fun main() {
println("Edit Distance between 'kitten' and 'sitting': ${editDistance("kitten", "sitting")}")
println("Edit Distance between 'horse' and 'ros': ${editDistance("horse", "ros")}")
}Swift implementation
func editDistance(_ word1: String, _ word2: String) -> Int {
let m = word1.count, n = word2.count
let w1 = Array(word1), w2 = Array(word2)
var dp = Array(repeating: Array(repeating: 0, count: n + 1), count: m + 1)
for i in 0...m { dp[i][0] = i }
for j in 0...n { dp[0][j] = j }
for i in 1...m {
for j in 1...n {
if w1[i - 1] == w2[j - 1] {
dp[i][j] = dp[i - 1][j - 1]
} else {
dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
}
}
}
return dp[m][n]
}
print("Edit Distance between 'kitten' and 'sitting':", editDistance("kitten", "sitting"))
print("Edit Distance between 'horse' and 'ros':", editDistance("horse", "ros"))TypeScript implementation
function editDistance(word1: string, word2: string): number {
const m = word1.length, n = word2.length;
const dp: number[][] = 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], dp[i][j - 1], dp[i - 1][j - 1]);
}
}
}
return dp[m][n];
}
console.log("Edit Distance between 'kitten' and 'sitting':", editDistance("kitten", "sitting"));
console.log("Edit Distance between 'horse' and 'ros':", editDistance("horse", "ros"));