From 5561157bbe36013d50804dd81ab658be28467683 Mon Sep 17 00:00:00 2001 From: tanyagupta01 Date: Sat, 15 Jun 2024 17:12:27 +0530 Subject: [PATCH 1/4] Create 0072-edit-distance.md --- .../0000-0099/0072-edit-distance.md | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md diff --git a/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md b/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md new file mode 100644 index 000000000..3e05c1df6 --- /dev/null +++ b/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md @@ -0,0 +1,176 @@ +--- +id: edit-distance +title: Edit Distance(LeetCode) +sidebar_label: 0072-Edit-Distance +tags: + - String + - Dynamic Programming +description: Given two strings `word1` and `word2`, return the minimum number of operations required to convert `word1` to `word2`. +sidebar_position: 72 +--- + +## Problem Statement + +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: + +- Insert a character +- Delete a character +- Replace a character + +### Examples + +**Example 1:** + +```plaintext +Input: word1 = "horse", word2 = "ros" +Output: 3 +Explanation: +horse -> rorse (replace 'h' with 'r') +rorse -> rose (remove 'r') +rose -> ros (remove 'e') +``` + +**Example 2:** + +```plaintext +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 + +We explore two main approaches: Recursive Dynamic Programming with +Memoization and Iterative Dynamic Programming with Tabulation. + +### Approach 1: Recursive Dynamic Programming (Memoization) +Concept: Store the solutions for each position to avoid redundant calculations. + +#### Algorithm + +For every index of string S1, we have three options to match that index with string S2, i.e replace the character, remove the character or insert some character at that index. Therefore, we can think in terms of string matching path as we have done already in previous questions. + +As there is no uniformity in data, there is no other way to find out than to try out all possible ways. To do so we will need to use recursion. + +Steps to memoize a recursize solution: +* Create a dp array of size [n][m]. The size of S1 and S2 are n and m respectively, so the variable i will always lie between ‘0’ and ‘n-1’ and the variable j between ‘0’ and ‘m-1’. +* * We initialize the dp array to -1. +Whenever we want to find the answer to particular parameters (say f(i,j)), we first check whether the answer is already calculated using the dp array(i.e dp[i][j]!= -1 ). If yes, simply return the value from the dp array. +* If not, then we are finding the answer for the given value for the first time, we will use the recursive relation as usual but before returning from the function, we will set dp[i][j] to the solution we get. + +#### Implementation + +```C++ +int editDistanceUtil(string& S1, string& S2, int i, int j, vector>& dp) { + // Base cases + if (i < 0) + return j + 1; + if (j < 0) + return i + 1; + + // If the result for this state has already been calculated, return it + if (dp[i][j] != -1) + return dp[i][j]; + + // If the characters at the current positions match, no operation is needed + if (S1[i] == S2[j]) + return dp[i][j] = 0 + editDistanceUtil(S1, S2, i - 1, j - 1, dp); + + // Minimum of three choices: + // 1. Replace the character at S1[i] with the character at S2[j] + // 2. Delete the character at S1[i] + // 3. Insert the character at S2[j] into S1 + else + return dp[i][j] = 1 + min(editDistanceUtil(S1, S2, i - 1, j - 1, dp), + min(editDistanceUtil(S1, S2, i - 1, j, dp), + editDistanceUtil(S1, S2, i, j - 1, dp))); +} + +// Function to calculate the minimum number of operations required to transform S1 into S2 +int editDistance(string& S1, string& S2) { + int n = S1.size(); + int m = S2.size(); + + // Create a DP table to memoize results + vector> dp(n, vector(m, -1)); + + // Call the utility function with the last indices of both strings + return editDistanceUtil(S1, S2, n - 1, m - 1, dp); +} +``` + +### Complexity Analysis + +- **Time complexity**: O(N*M) +Reason: There are N*M states therefore at max ‘N*M’ new problems will be solved. +- **Space complexity**: O(N*M) + O(N+M) +Reason: We are using a recursion stack space(O(N+M)) and a 2D array ( O(N*M)). + +### Approach 2: Iterative Dynamic Programming (Tabulation) + +Concept: In the recursive logic, we set the base case too if(i<0 ) and if(j<0) but we can’t set the dp array’s index to -1. Therefore a hack for this issue is to shift every index by 1 towards the right. + +#### Algorithm + +1. First we initialise the dp array of size [n+1][m+1] as zero. +2. Next, we set the base condition (keep in mind 1-based indexing), we set the first column’s value as i and the first row as j( 1-based indexing). +3. Similarly, we will implement the recursive code by keeping in mind the shifting of indexes, therefore S1[i] will be converted to S1[i-1]. Same for S2. +4. At last, we will print dp[N][M] as our answer.required. + +#### Implementation + +```C++ +int editDistance(string& S1, string& S2) { + int n = S1.size(); + int m = S2.size(); + + // Create a DP table to store edit distances + vector> dp(n + 1, vector(m + 1, 0)); + + // Initialize the first row and column + for (int i = 0; i <= n; i++) { + dp[i][0] = i; + } + for (int j = 0; j <= m; j++) { + dp[0][j] = j; + } + + // Fill in the DP table + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + if (S1[i - 1] == S2[j - 1]) { + // If the characters match, no additional cost + dp[i][j] = dp[i - 1][j - 1]; + } else { + // Minimum of three choices: + // 1. Replace the character at S1[i-1] with S2[j-1] + // 2. Delete the character at S1[i-1] + // 3. Insert the character at S2[j-1] into S1 + dp[i][j] = 1 + min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])); + } + } + } + + // The value at dp[n][m] contains the edit distance + return dp[n][m]; +} +``` + +### Complexity Analysis + +- **Time complexity**: O(N*M) +Reason: There are two nested loops +- **Space complexity**: O(N*M) +Reason: We are using an external array of size ‘N*M’. Stack Space is eliminated. From 74d53d4a7337536f169f2f88de7ebe009b771d3b Mon Sep 17 00:00:00 2001 From: tanyagupta01 Date: Sat, 15 Jun 2024 21:44:32 +0530 Subject: [PATCH 2/4] Update 0072-edit-distance.md --- .../lc-solutions/0000-0099/0072-edit-distance.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md b/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md index 3e05c1df6..2e35075b9 100644 --- a/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md +++ b/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md @@ -65,48 +65,38 @@ For every index of string S1, we have three options to match that index with str As there is no uniformity in data, there is no other way to find out than to try out all possible ways. To do so we will need to use recursion. Steps to memoize a recursize solution: -* Create a dp array of size [n][m]. The size of S1 and S2 are n and m respectively, so the variable i will always lie between ‘0’ and ‘n-1’ and the variable j between ‘0’ and ‘m-1’. -* * We initialize the dp array to -1. +- Create a dp array of size [n][m]. The size of S1 and S2 are n and m respectively, so the variable i will always lie between ‘0’ and ‘n-1’ and the variable j between ‘0’ and ‘m-1’. +- We initialize the dp array to -1. Whenever we want to find the answer to particular parameters (say f(i,j)), we first check whether the answer is already calculated using the dp array(i.e dp[i][j]!= -1 ). If yes, simply return the value from the dp array. -* If not, then we are finding the answer for the given value for the first time, we will use the recursive relation as usual but before returning from the function, we will set dp[i][j] to the solution we get. +- If not, then we are finding the answer for the given value for the first time, we will use the recursive relation as usual but before returning from the function, we will set dp[i][j] to the solution we get. #### Implementation ```C++ int editDistanceUtil(string& S1, string& S2, int i, int j, vector>& dp) { - // Base cases if (i < 0) return j + 1; if (j < 0) return i + 1; - // If the result for this state has already been calculated, return it if (dp[i][j] != -1) return dp[i][j]; - // If the characters at the current positions match, no operation is needed if (S1[i] == S2[j]) return dp[i][j] = 0 + editDistanceUtil(S1, S2, i - 1, j - 1, dp); - // Minimum of three choices: - // 1. Replace the character at S1[i] with the character at S2[j] - // 2. Delete the character at S1[i] - // 3. Insert the character at S2[j] into S1 else return dp[i][j] = 1 + min(editDistanceUtil(S1, S2, i - 1, j - 1, dp), min(editDistanceUtil(S1, S2, i - 1, j, dp), editDistanceUtil(S1, S2, i, j - 1, dp))); } -// Function to calculate the minimum number of operations required to transform S1 into S2 int editDistance(string& S1, string& S2) { int n = S1.size(); int m = S2.size(); - // Create a DP table to memoize results vector> dp(n, vector(m, -1)); - // Call the utility function with the last indices of both strings return editDistanceUtil(S1, S2, n - 1, m - 1, dp); } ``` From 682d5599d4be914479f7553bd32f838d7c48f422 Mon Sep 17 00:00:00 2001 From: tanyagupta01 Date: Sat, 15 Jun 2024 22:35:42 +0530 Subject: [PATCH 3/4] Update 0072-edit-distance.md --- dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md b/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md index 2e35075b9..aba0522a8 100644 --- a/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md +++ b/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md @@ -110,7 +110,7 @@ Reason: We are using a recursion stack space(O(N+M)) and a 2D array ( O(N*M)). ### Approach 2: Iterative Dynamic Programming (Tabulation) -Concept: In the recursive logic, we set the base case too if(i<0 ) and if(j<0) but we can’t set the dp array’s index to -1. Therefore a hack for this issue is to shift every index by 1 towards the right. +Concept: In the recursive logic, we set the base case too `if(i<0)` and if(j<0) but we can’t set the dp array’s index to -1. Therefore a hack for this issue is to shift every index by 1 towards the right. #### Algorithm From 78bb002a52325ce8ffa0d43d34eb306f9d16cae6 Mon Sep 17 00:00:00 2001 From: Ajay Dhangar <99037494+Ajay-Dhangar@users.noreply.github.com> Date: Sun, 16 Jun 2024 05:53:56 +0530 Subject: [PATCH 4/4] Update 0072-edit-distance.md --- .../lc-solutions/0000-0099/0072-edit-distance.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md b/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md index aba0522a8..ce6fc936e 100644 --- a/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md +++ b/dsa-solutions/lc-solutions/0000-0099/0072-edit-distance.md @@ -5,7 +5,7 @@ sidebar_label: 0072-Edit-Distance tags: - String - Dynamic Programming -description: Given two strings `word1` and `word2`, return the minimum number of operations required to convert `word1` to `word2`. +description: Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2. sidebar_position: 72 --- @@ -110,7 +110,7 @@ Reason: We are using a recursion stack space(O(N+M)) and a 2D array ( O(N*M)). ### Approach 2: Iterative Dynamic Programming (Tabulation) -Concept: In the recursive logic, we set the base case too `if(i<0)` and if(j<0) but we can’t set the dp array’s index to -1. Therefore a hack for this issue is to shift every index by 1 towards the right. +Concept: In the recursive logic, we set the base case to `if(i<0)` and `if(j<0)` but we can’t set the dp array’s index to -1. Therefore a hack for this issue is to shift every index by 1 towards the right. #### Algorithm @@ -160,7 +160,7 @@ int editDistance(string& S1, string& S2) { ### Complexity Analysis -- **Time complexity**: O(N*M) +- **Time complexity**: $O(N \times M)$ Reason: There are two nested loops -- **Space complexity**: O(N*M) +- **Space complexity**: $O(N \times M)$ Reason: We are using an external array of size ‘N*M’. Stack Space is eliminated.