Edit Distance | DP using Memoization

20 May, 2022
Given two strings str1 and str2 and below operations that can be performed on str1. Find the minimum number of edits (operations) required to convert ‘str1’ into ‘str2’. 
Insert
Remove
Replace
All of the above operations are of equal cost. 
Examples: 
Input: str1 = “geek”, str2 = “gesek” Output: 1 We can convert str1 into str2 by inserting a ‘s’.
Input: str1 = “cat”, str2 = “cut” Output: 1 We can convert str1 into str2 by replacing ‘a’ with ‘u’.
Input: str1 = “sunday”, str2 = “saturday” Output: 3 Last three and first characters are same. We basically need to convert “un” to “atur”. This can be done using below three operations. Replace ‘n’ with ‘r’, insert t, insert a
What are the subproblems in this case? The idea is to process all characters one by one starting from either from left or right sides of both strings. Let us traverse from right corner, there are two possibilities for every pair of characters being traversed. The following are the conditions: 
If last characters of two strings are same, nothing much to do. Ignore last characters and get count for remaining strings. So we recur for lengths m-1 and n-1.Else (If last characters are not same), we consider all operations on ‘str1’, consider all three operations on last character of first string, recursively compute minimum cost for all three operations, and take minimum of three values. Insert: Recur for m and n-1Remove: Recur for m-1 and nReplace: Recur for m-1 and n-1
If last characters of two strings are same, nothing much to do. Ignore last characters and get count for remaining strings. So we recur for lengths m-1 and n-1.
Else (If last characters are not same), we consider all operations on ‘str1’, consider all three operations on last character of first string, recursively compute minimum cost for all three operations, and take minimum of three values. Insert: Recur for m and n-1Remove: Recur for m-1 and nReplace: Recur for m-1 and n-1
Insert: Recur for m and n-1
Remove: Recur for m-1 and n
Replace: Recur for m-1 and n-1
Below is the implementation of the above approach: 
C++
Java
Python
C#
Javascript
// A Naive recursive C++ program to find minimum number// operations to convert str1 to str2#include <bits/stdc++.h>using namespace std; // Utility function to find minimum of three numbersint min(int x, int y, int z){    return min(min(x, y), z);} int editDist(string str1, string str2, int m, int n){    // If first string is empty, the only option is to    // insert all characters of second string into first    if (m == 0)        return n;     // If second string is empty, the only option is to    // remove all characters of first string    if (n == 0)        return m;     // If last characters of two strings are same, nothing    // much to do. Ignore last characters and get count for    // remaining strings.    if (str1[m - 1] == str2[n - 1])        return editDist(str1, str2, m - 1, n - 1);     // If last characters are not same, consider all three    // operations on last character of first string, recursively    // compute minimum cost for all three operations and take    // minimum of three values.    return 1 + min(editDist(str1, str2, m, n - 1), // Insert                   editDist(str1, str2, m - 1, n), // Remove                   editDist(str1, str2, m - 1, n - 1) // Replace                   );} // Driver programint main(){    // your code goes here    string str1 = "sunday";    string str2 = "saturday";     cout << editDist(str1, str2, str1.length(), str2.length());     return 0;}
// A Naive recursive Java program to find minimum number// operations to convert str1 to str2class EDIST {    static int min(int x, int y, int z)    {        if (x <= y && x <= z)            return x;        if (y <= x && y <= z)            return y;        else            return z;    }     static int editDist(String str1, String str2, int m, int n)    {        // If first string is empty, the only option is to        // insert all characters of second string into first        if (m == 0)            return n;         // If second string is empty, the only option is to        // remove all characters of first string        if (n == 0)            return m;         // If last characters of two strings are same, nothing        // much to do. Ignore last characters and get count for        // remaining strings.        if (str1.charAt(m - 1) == str2.charAt(n - 1))            return editDist(str1, str2, m - 1, n - 1);         // If last characters are not same, consider all three        // operations on last character of first string, recursively        // compute minimum cost for all three operations and take        // minimum of three values.        return 1 + min(editDist(str1, str2, m, n - 1), // Insert                       editDist(str1, str2, m - 1, n), // Remove                       editDist(str1, str2, m - 1, n - 1) // Replace                       );    }     public static void main(String args[])    {        String str1 = "sunday";        String str2 = "saturday";         System.out.println(editDist(str1, str2, str1.length(), str2.length()));    }}
# A Naive recursive Python program to find minimum number# operations to convert str1 to str2def editDistance(str1, str2, m, n):     # If first string is empty, the only option is to    # insert all characters of second string into first    if m == 0:         return n     # If second string is empty, the only option is to    # remove all characters of first string    if n == 0:        return m     # If last characters of two strings are same, nothing    # much to do. Ignore last characters and get count for    # remaining strings.    if str1[m-1]== str2[n-1]:        return editDistance(str1, str2, m-1, n-1)     # If last characters are not same, consider all three    # operations on last character of first string, recursively    # compute minimum cost for all three operations and take    # minimum of three values.    return 1 + min(editDistance(str1, str2, m, n-1),    # Insert                   editDistance(str1, str2, m-1, n),    # Remove                   editDistance(str1, str2, m-1, n-1)    # Replace                   ) # Driver program to test the above functionstr1 = "sunday"str2 = "saturday"print editDistance(str1, str2, len(str1), len(str2))
// A Naive recursive C# program to// find minimum numberoperations// to convert str1 to str2using System; class GFG {    static int min(int x, int y, int z)    {        if (x <= y && x <= z)            return x;        if (y <= x && y <= z)            return y;        else            return z;    }     static int editDist(String str1, String str2, int m, int n)    {        // If first string is empty, the only option is to        // insert all characters of second string into first        if (m == 0)            return n;         // If second string is empty, the only option is to        // remove all characters of first string        if (n == 0)            return m;         // If last characters of two strings are same, nothing        // much to do. Ignore last characters and get count for        // remaining strings.        if (str1[m - 1] == str2[n - 1])            return editDist(str1, str2, m - 1, n - 1);         // If last characters are not same, consider all three        // operations on last character of first string, recursively        // compute minimum cost for all three operations and take        // minimum of three values.        return 1 + min(editDist(str1, str2, m, n - 1), // Insert                       editDist(str1, str2, m - 1, n), // Remove                       editDist(str1, str2, m - 1, n - 1) // Replace                       );    }     // Driver code    public static void Main()    {        String str1 = "sunday";        String str2 = "saturday";        Console.WriteLine(editDist(str1, str2, str1.Length,                                   str2.Length));    }}
<script> // A Naive recursive Javascript// program to find minimum number// operations to convert str1 to str2 // Utility function to find minimum of three numbersfunction min(x, y, z){    return Math.min(Math.min(x, y), z);} function editDist(str1, str2, m, n){    // If first string is empty, the only option is to    // insert all characters of second string into first    if (m == 0)        return n;     // If second string is empty, the only option is to    // remove all characters of first string    if (n == 0)        return m;     // If last characters of two strings are same, nothing    // much to do. Ignore last characters and get count for    // remaining strings.    if (str1[m - 1] == str2[n - 1])        return editDist(str1, str2, m - 1, n - 1);     // If last characters are not same, consider all three    // operations on last character of first string, recursively    // compute minimum cost for all three operations and take    // minimum of three values.    return 1 + min(editDist(str1, str2, m, n - 1), // Insert                   editDist(str1, str2, m - 1, n), // Remove                   editDist(str1, str2, m - 1, n - 1) // Replace                   );} // Driver program// your code goes herevar str1 = "sunday";var str2 = "saturday";document.write( editDist(str1, str2, str1.length, str2.length)); </script>
Output: 
3
The time complexity of above solution is O(3^n) which is exponential. The worst-case happens when none of characters of two strings match. Below is a recursive call diagram for worst case.  
We can see that many subproblems are solved, again and again, for example, eD(2, 2) is called three times. Since same subproblems are called again, this problem has Overlapping Subproblems property. So Edit Distance problem has both properties (see this and this) of a dynamic programming problem. Like other typical Dynamic Programming(DP) problems, recomputations of same subproblems can be avoided by constructing a temporary array that stores results of subproblems. The bottom-up approach can be found here.
The problem can also be solved using top-down Dynamic Programming and using memoization. In the recursive code, memoization can be used to avoid overlapping problems. There are several repetitive calls which can be computed in O(1) if the value is stored when called for the first time. On observing the recursive code, it is seen that a maximum of two parameters is changing their value on every recursive call. There will be cases when the same recursive call has been called previously. Since two parameters are not constant, a 2-D array can be used to avoid repetitive calls. Hence the return value is stored in some 2-D array. Below are the steps: 
Initialize a 2-D DP array of size m *n with -1 at all the index.
On every recursive call, store the return value at dp[m][n] so that if func(m, n) is called again, it can be answered in O(1) without using recursion.
Check if the recursive call has been visited previously or not by checking the value at dp[m][n].
Below is the implementation of the above approach:  
C++
Java
Python3
Javascript
// A memoization program to find minimum number// operations to convert str1 to str2#include <bits/stdc++.h>using namespace std; // Maximum 2-D array column sizeconst int maximum = 1000; // Utility function to find minimum of three numbersint min(int x, int y, int z){    return min(min(x, y), z);} int editDist(string str1, string str2, int m, int n, int dp[][maximum]){    // If first string is empty, the only option is to    // insert all characters of second string into first    if (m == 0)        return n;     // If second string is empty, the only option is to    // remove all characters of first string    if (n == 0)        return m;     // if the recursive call has been    // called previously, then return    // the stored value that was calculated    // previously    if (dp[m - 1][n - 1] != -1)        return dp[m - 1][n - 1];     // If last characters of two strings are same, nothing    // much to do. Ignore last characters and get count for    // remaining strings.     // Store the returned value at dp[m-1][n-1]    // considering 1-based indexing    if (str1[m - 1] == str2[n - 1])        return dp[m - 1][n - 1] = editDist(str1, str2, m - 1, n - 1, dp);     // If last characters are not same, consider all three    // operations on last character of first string, recursively    // compute minimum cost for all three operations and take    // minimum of three values.     // Store the returned value at dp[m-1][n-1]    // considering 1-based indexing    return dp[m - 1][n - 1] = 1 + min(editDist(str1, str2, m, n - 1, dp), // Insert                                      editDist(str1, str2, m - 1, n, dp), // Remove                                      editDist(str1, str2, m - 1, n - 1, dp) // Replace                                      );} // Driver Codeint main(){     string str1 = "sunday";    string str2 = "saturday";    int m = str1.length();    int n = str2.length();     // Declare a dp array which stores    // the answer to recursive calls    int dp[m][maximum];     // initially all index with -1    memset(dp, -1, sizeof dp);     // Function call    // memoization and top-down approach    cout << editDist(str1, str2, m, n, dp);     return 0;}
// A memoization program to find minimum number// operations to convert str1 to str2import java.util.*; class GFG{     // Maximum 2-D array column size  static int maximum = 1000;   // Utility function to find minimum of three numbers  static int min(int x, int y, int z)  {    return Math.min((Math.min(x, y)), z);  }   static int editDist(String str1, String str2, int m,                      int n, int[][] dp)  {         // If first string is empty, the only option is to    // insert all characters of second string into first    if (m == 0)      return n;     // If second string is empty, the only option is to    // remove all characters of first string    if (n == 0)      return m;     // if the recursive call has been    // called previously, then return    // the stored value that was calculated    // previously    if (dp[m - 1][n - 1] != -1)      return dp[m - 1][n - 1];     // If last characters of two strings are same,    // nothing much to do. Ignore last characters and    // get count for remaining strings.     // Store the returned value at dp[m-1][n-1]    // considering 1-based indexing    if (str1.charAt(m - 1) == str2.charAt(n - 1))      return dp[m - 1][n - 1]      = editDist(str1, str2, m - 1, n - 1, dp);     // If last characters are not same, consider all    // three operations on last character of first    // string, recursively compute minimum cost for all    // three operations and take minimum of three    // values.     // Store the returned value at dp[m-1][n-1]    // considering 1-based indexing    return dp[m - 1][n - 1]      = 1      + min(editDist(str1, str2, m, n - 1,                     dp), // Insert            editDist(str1, str2, m - 1, n,                     dp), // Remove            editDist(str1, str2, m - 1, n - 1,                     dp) // Replace           );  }   // Driver code  public static void main(String[] args)  {    String str1 = "sunday";    String str2 = "saturday";    int m = str1.length();    int n = str2.length();     // Declare a dp array which stores    // the answer to recursive calls    int[][] dp = new int[m][maximum];    for (int i = 0; i < dp.length; i++)      Arrays.fill(dp[i], -1);     // Function call    // memoization and top-down approach    System.out.println(editDist(str1, str2, m, n, dp));  }} // This code is contributed by Karandeep Singh
# A memoization program to find minimum number# operations to convert str1 to str2def editDistance(str1, str2, m, n, d = {}):         key = m, n     # If first string is empty, the only option    # is to insert all characters of second    # string into first    if m == 0:        return n     # If second string is empty, the only    # option is to remove all characters    # of first string    if n == 0:        return m     if key in d:        return d[key]             # If last characters of two strings are same,    # nothing much to do. Ignore last characters    # and get count for remaining strings.    if str1[m - 1] == str2[n - 1]:        return editDistance(str1, str2, m - 1, n - 1)     # If last characters are not same, consider    # all three operations on last character of    # first string, recursively compute minimum    # cost for all three operations and take    # minimum of three values.         # Store the returned value at dp[m-1][n-1]    # considering 1-based indexing    d[key] = 1 + min(editDistance(str1, str2, m, n - 1), # Insert                     editDistance(str1, str2, m - 1, n), # Remove                     editDistance(str1, str2, m - 1, n - 1)) # Replace    return d[key] # Driver codestr1 = "sunday"str2 = "saturday" print(editDistance(str1, str2, len(str1), len(str2))) # This code is contributed by puranjanprithu
<script> // A memoization program to find minimum number// operations to convert str1 to str2 // Maximum 2-D array column sizeconst maximum = 1000; // Utility function to find minimum of three numbersfunction min(x, y, z){    return Math.min(Math.min(x, y), z);} function editDist(str1, str2, m, n, dp){    // If first string is empty, the only option is to    // insert all characters of second string into first    if (m == 0)        return n;     // If second string is empty, the only option is to    // remove all characters of first string    if (n == 0)        return m;     // if the recursive call has been    // called previously, then return    // the stored value that was calculated    // previously    if (dp[m - 1][n - 1] != -1)        return dp[m - 1][n - 1];     // If last characters of two strings are same, nothing    // much to do. Ignore last characters and get count for    // remaining strings.     // Store the returned value at dp[m-1][n-1]    // considering 1-based indexing    if (str1[m - 1] == str2[n - 1])        return dp[m - 1][n - 1] = editDist(str1, str2, m - 1, n - 1, dp);     // If last characters are not same, consider all three    // operations on last character of first string, recursively    // compute minimum cost for all three operations and take    // minimum of three values.     // Store the returned value at dp[m-1][n-1]    // considering 1-based indexing    return dp[m - 1][n - 1] = 1 + min(editDist(str1, str2, m, n - 1, dp), // Insert                                    editDist(str1, str2, m - 1, n, dp), // Remove                                    editDist(str1, str2, m - 1, n - 1, dp) // Replace                  );} // Driver Code let str1 = "sunday";let str2 = "saturday";let m = str1.length;let n = str2.length; // Declare a dp array which stores// the answer to recursive calls// initially all index with -1let dp = new Array(m);for(let i = 0; i < m; i++){  dp[i] = new Array(maximum).fill(-1);}  // Function call// memoization and top-down approachdocument.write(editDist(str1, str2, m, n, dp)); // This code is contributed by shinjanpatra </script>
Output: 
3
Time Complexity: O(M * N) Auxiliary Space: O(M * N) 
nidhi_biet
puranjanprithu
sweetyty
famously
karandeep1234
shinjanpatra
Memoization
Dynamic Programming
Strings
Strings
Dynamic Programming
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Subset Sum Problem | DP-25
Longest Palindromic Substring | Set 1
Floyd Warshall Algorithm | DP-16
Find if there is a path between two vertices in an undirected graph
Matrix Chain Multiplication | DP-8
Write a program to reverse an array or string
Reverse a string in Java
Write a program to print all permutations of a given string
C++ Data Types
Check for Balanced Brackets in an expression (well-formedness) using Stack