[LintCode] Edit Distance
Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)
You have the following 3 operations permitted on a word:
- Insert a character
- Delete a character
- Replace a character
Given word1 = "mart"
and word2 = "karma"
, return 3
.
Solution 1. Recursion
i is the last character's index in word1, j is the last character's index in word2.
Recursive functions:
f(i, j) = Math.min(1 + Math.min(f (i - 1, j), f(i, j - 1) ), f(i - 1, j - 1)), if word1.charAt(i) == word2.charAt(j);
f(i, j) = Math.min(1 + Math.min(f (i - 1, j), f(i, j - 1) ), 1 + f(i - 1, j - 1)), if word1.charAt(i) != word2.charAt(j);
Base case:
i < 0 && j < 0: both words are empty, no operations is needed;
i < 0 && j >= 0: word1 empty but word2 not empty, j + 1 deletions is needed;
i >= 0 && j < 0: word1 not empty word2 empty, i + 1 deletions is needed;
1 public class Solution { 2 public int minDistance(String word1, String word2) { 3 if(word1 == null || word2 == null){ 4 return Integer.MAX_VALUE; 5 } 6 return helper(word1, word1.length() - 1, word2, word2.length() - 1); 7 } 8 private int helper(String word1, int idx1, String word2, int idx2){ 9 if(idx1 < 0 && idx2 < 0){ 10 return 0; 11 } 12 else if(idx1 < 0){ 13 return idx2 + 1; 14 } 15 else if(idx2 < 0){ 16 return idx1 + 1; 17 } 18 if(word1.charAt(idx1) == word2.charAt(idx2)){ 19 return Math.min(1 + Math.min(helper(word1, idx1- 1, word2, idx2), helper(word1, idx1, word2, idx2 - 1)), helper(word1, idx1 - 1, word2, idx2 - 1)); 20 } 21 return Math.min(1 + Math.min(helper(word1, idx1- 1, word2, idx2), helper(word1, idx1, word2, idx2 - 1)), 1 + helper(word1, idx1 - 1, word2, idx2 - 1)); 22 } 23 }
Solution 2. Top down DFS search with Memoization, (Dynamic Programming in essence)
Solution 1 does a lot of duplicate work by computing answers of the same subproblems over and over.
Caching subproblems' results with memoization optimizes on solution 1.
1 public class Solution { 2 private int[][] T; 3 public int minDistance(String word1, String word2) { 4 if(word1 == null || word2 == null){ 5 return Integer.MAX_VALUE; 6 } 7 T = new int[word1.length()][word2.length()]; 8 for(int i = 0; i < word1.length(); i++){ 9 for(int j = 0; j < word2.length(); j++){ 10 T[i][j] = Integer.MAX_VALUE; 11 } 12 } 13 return helper(word1, word1.length() - 1, word2, word2.length() - 1); 14 } 15 private int helper(String word1, int idx1, String word2, int idx2){ 16 if(idx1 < 0 && idx2 < 0){ 17 return 0; 18 } 19 else if(idx1 < 0){ 20 return idx2 + 1; 21 } 22 else if(idx2 < 0){ 23 return idx1 + 1; 24 } 25 if(T[idx1][idx2] != Integer.MAX_VALUE){ 26 return T[idx1][idx2]; 27 } 28 int r = Integer.MAX_VALUE; 29 if(word1.charAt(idx1) == word2.charAt(idx2)){ 30 r = Math.min(1 + Math.min(helper(word1, idx1- 1, word2, idx2), helper(word1, idx1, word2, idx2 - 1)), helper(word1, idx1 - 1, word2, idx2 - 1)); 31 } 32 else{ 33 r = Math.min(1 + Math.min(helper(word1, idx1- 1, word2, idx2), helper(word1, idx1, word2, idx2 - 1)), 1 + helper(word1, idx1 - 1, word2, idx2 - 1)); 34 } 35 T[idx1][idx2] = r; 36 return r; 37 } 38 }
Solution 3. Bottom up dynamic programming
State functions are derived directly from the above recursive solution.
dp[i][j]: the minimum number of operations needed to covert word1's first ith characters to word2's first jth characters.
Function:
if(word1.charAt(i - 1) == word2.charAt(j - 1)){ dp[i][j] = Math.min(1 + Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]); } else{ dp[i][j] = 1 + Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]); }
Initializations:
dp[i][0]: delete a character from word1 i times;
dp[0][j]: insert a character to word1 j times;
1 public class Solution { 2 public int minDistance(String word1, String word2) { 3 if(word1 == null || word2 == null){ 4 return Integer.MAX_VALUE; 5 } 6 int[][] dp = new int[word1.length() + 1][word2.length() + 1]; 7 dp[0][0] = 0; 8 for(int i = 1; i <= word1.length(); i++){ 9 dp[i][0] = i; 10 } 11 for(int j = 1; j <= word2.length(); j++){ 12 dp[0][j] = j; 13 } 14 for(int i = 1; i <= word1.length(); i++){ 15 for(int j = 1; j <= word2.length(); j++){ 16 if(word1.charAt(i - 1) == word2.charAt(j - 1)){ 17 dp[i][j] = Math.min(1 + Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]); 18 } 19 else{ 20 dp[i][j] = 1 + Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]); 21 } 22 } 23 } 24 return dp[word1.length()][word2.length()]; 25 } 26 }
Related Problems