Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 43

Dynamic Programming: Edit Distance

Recommended resources

Hello! Welcome to the final lesson in our module on Dynamic Programming.

In the last two lessons, we've built a solid foundation by solving the 0/1 Knapsack and Longest Common Subsequence (LCS) problems. You've seen how to break down complex problems into simpler, overlapping subproblems and use memoization or tabulation to arrive at an efficient solution. As we previewed, today's topic, the Edit Distance problem, is a perfect illustration of how a small change in perspective can adapt a known pattern to solve a new challenge.

This lesson will teach you how to find the minimum number of operations required to convert one string into another. By the end, you'll be able to solve the Edit Distance problem by modifying the recurrence relation we used for LCS and applying the same dynamic programming techniques you've already mastered.

1. Understanding the Edit Distance Problem

The Edit Distance—also known as the Levenshtein distance—measures the "difference" between two sequences. Given two strings, s1 and s2, the goal is to find the minimum number of single-character edits required to change s1 into s2. The allowed edits are:

  1. Insert a character.
  2. Delete a character.
  3. Replace a character.

This algorithm has practical applications, from spell checkers suggesting corrections to bioinformatics measuring the similarity between DNA sequences.

DP 33. Edit Distance | Recursive to 1D Array Optimised Solution 🔥

Let's begin with a clear definition of the problem and the three allowed operations. This video from the 'take U forward' channel provides a great introduction with a couple of illustrative examples.

Watch the first 5 minutes and 34 seconds of this video (00:00 - 05:34). Pay close attention to how the examples (transforming 'horse' to 'ros' and 'intention' to 'execution') are broken down into a sequence of insert, delete, and replace operations.


2. The Recurrence Relation: The Heart of the Solution

Just like with LCS, we will solve this by comparing the strings from their ends, using indices i for s1 and j for s2. Let dp[i][j] be the minimum edit distance required to convert the prefix s1[0...i-1] (length i) into s2[0...j-1] (length j).

We have two main cases when we compare the characters s1[i-1] and s2[j-1]:

Case 1: The characters match (s1[i-1] == s2[j-1])
This is the easy case. If the last characters are the same, no operation is needed for them. The cost is 0. The total edit distance is simply the edit distance of the strings without these last characters. This is identical to the matching case in LCS.

Case 2: The characters do not match (s1[i-1] != s2[j-1])
Here's where Edit Distance differs from LCS. We must perform an operation, and each operation has a cost of 1. We have three choices, and we'll pick the one that results in the minimum overall cost:

  1. Insert: We can insert the character s2[j-1] into s1 to match it. Now, s1 has been transformed to match up to s2[j-1], so we only need to solve the subproblem for s1[0...i-1] and s2[0...j-2]. The cost is 1 + dp[i][j-1].
  2. Delete: We can delete the character s1[i-1] from s1. Now, we need to solve the subproblem of transforming s1[0...i-2] into s2[0...j-1]. The cost is 1 + dp[i-1][j].
  3. Replace: We can replace s1[i-1] with s2[j-1]. Now these characters match, so we solve the subproblem for s1[0...i-2] and s2[0...j-2]. The cost is 1 + dp[i-1][j-1].

Since we want the minimum number of operations, we take the minimum of these three choices.

Base Cases:

  • If s1 is empty (i=0), we must insert all j characters of s2. So, dp[0][j] = j.
  • If s2 is empty (j=0), we must delete all i characters of s1. So, dp[i][0] = i.

This complete recurrence relation is all we need.

DP 33. Edit Distance | Recursive to 1D Array Optimised Solution 🔥

Understanding this recurrence is the most critical part of the lesson. The same video provides a meticulous walkthrough of deriving this logic.

Continue watching from 05:34 to 21:58. The video first explores the recursive intuition and then formally defines the function f(i, j) and derives the recurrence for both matching and non-matching characters, including the base cases. Don't worry about the code yet, just focus on the logic.


3. Implementation: Tabulation

As with our previous DP problems, a naive recursive solution would be highly inefficient due to overlapping subproblems. We will proceed directly to the tabulation (bottom-up) approach, as it's often more intuitive to implement iteratively and avoids recursion depth limits.

We will create a 2D DP table of size (m+1) x (n+1), where m and n are the lengths of s1 and s2.

  1. Initialization: We fill the first row and first column based on our base cases. dp[i][0] = i and dp[0][j] = j.
  2. Iteration: We iterate through the table from dp[1][1] to dp[m][n], filling each cell dp[i][j] using the recurrence relation we just defined.
Levenshtein Distance Dynamic Programming Table and Code
This image shows a DP table being filled to calculate the edit distance. Each cell `dp[i][j]` will contain the minimum cost to transform the first `i` characters of the source string into the first `j` characters of the target string. The value in a cell is derived from its top, left, and top-left neighbors, representing the costs of deletion, insertion, and replacement/match, respectively.

Edit Distance Between 2 Strings - The Levenshtein Distance ("Edit Distance" on LeetCode)

Now, let's see how to translate this logic into a bottom-up DP table. This video from Back To Back SWE offers an excellent, intuitive visualization of filling out the table cell by cell.

Watch from 07:58 to 14:37. The key takeaway is understanding what each cell represents (a subproblem) and how the final answer is built upon the solutions to smaller subproblems. The video clearly explains how the three neighboring cells correspond to our three operations (delete, insert, replace).


4. Code and Complexity

Now that you've seen the logic for both the recurrence and the table-filling process, we can put it all together in code. We'll examine a complete tabulation solution.

Edit Distance - Tabulation Approach

This article from GeeksForGeeks provides a clean, well-explained tabulation implementation and analyzes its performance.

Read the section titled '[Better Approach 2] Using Bottom-Up DP (Tabulation)'. Review the C++, Java, or Python code to see the direct implementation of the table-filling logic. Confirm the time and space complexity.

As the article confirms, the complexity of this solution is:

  • Time Complexity: . We must fill every cell of the (m+1) x (n+1) table, and each calculation is a constant-time operation.
  • Space Complexity: . We need to store the entire DP table.
Test your understanding!

You are calculating the edit distance from s1 = "SATURDAY" to s2 = "SUNDAY". The DP table dp is being filled. What is the value of dp[3][3], which corresponds to transforming "SAT" into "SUN"?

Show answer

We need to find the edit distance between s1_prefix = "SAT" and s2_prefix = "SUN".
The last characters are T and N. They do not match.
So, we use the rule: dp[3][3] = 1 + min(dp[2][3], dp[3][2], dp[2][2]).

  • dp[2][3] (Delete 'A' from "SA" -> "S", then transform "S" to "SUN"): dp("SA", "SUN"). Cost is high.
  • dp[3][2] (Transform "SAT" to "SU"): dp("SAT", "SU"). Cost is also high.
  • dp[2][2] (Replace 'A' with 'U'): dp("SA", "SU").

Let's compute the dependencies:

  • dp[2][2] is the distance between "SA" and "SU". The last characters 'A' and 'U' differ.
    • dp[2][2] = 1 + min(dp(SA, S), dp(S, SU), dp(S, S))
    • dp("S", "S") is 0. So dp[2][2] = 1 + 0 = 1. (We replace 'A' with 'U').
  • dp[2][3] is dp("SA", "SUN"). 1 + min(dp(SA,SU), dp(S,SUN), dp(S,SU)). This will be 1 + dp(S,SU) which is 1+1=2.
  • dp[3][2] is dp("SAT", "SU"). 1 + min(dp(SAT,S), dp(SA,SU), dp(SA,S)). This will be 1 + dp(SA,SU) which is 1+1=2.

So, dp[3][3] = 1 + min(2, 2, 1) = 1 + 1 = 2.
The operations are: Replace 'A' with 'U', then Replace 'T' with 'N'. Total cost: 2.


5. Space Optimization

Just as with the LCS problem, if you look at the recurrence relation, you'll notice that to compute any cell dp[i][j], we only need values from the current row (j-1) and the previous row (i-1). We don't need the entire table.

This allows us to optimize the space complexity from down to (where n is the shorter string's length) by only keeping track of the previous and current rows.

DP 33. Edit Distance | Recursive to 1D Array Optimised Solution 🔥

Applying the space optimization trick we learned in the last lesson is a great way to reinforce the pattern. Let's see it applied to Edit Distance.

Watch the final part of the 'take U forward' video from 33:17 to 37:04. The instructor explains how to use just two 1D arrays (previous and current) to achieve the same result with less memory. This is a common and valuable optimization technique.


6. Going Further: Reconstructing the Edits

Our algorithm finds the minimum number of edits, but what if you wanted to know the actual sequence of operations? This is exactly what a diff tool does. By storing which choice (insert, delete, or replace) led to the minimum value at each step, we can backtrack through the DP table from the end to reconstruct the optimal path of edits.

Classic DP: Edit Distance - Further Exploration

For a deeper architectural understanding, let's explore how to get the specific edits, not just the count. This article from 'Labuladong' provides an excellent explanation and implementation for this advanced task.

This is an optional but highly recommended read. Focus on the section 'IV. Further Exploration'. Understand how augmenting the DP table to store the choice at each cell allows you to backtrack from dp[m][n] to dp[0][0] and print the sequence of operations.


Conclusion

Congratulations on completing this module on Dynamic Programming! You have now tackled three classic DP problems: 0/1 Knapsack, Longest Common Subsequence, and Edit Distance. You've seen how a consistent thought process can be applied to solve seemingly different problems efficiently.

Key Takeaways:

  • Edit Distance finds the minimum number of inserts, deletes, or replaces to transform one string into another.
  • The recurrence relation is very similar to LCS, but the "mismatch" case considers three operations (insert, delete, replace) and adds 1 to the cost of the optimal subproblem.
  • The problem is solved in time and space using tabulation, which can be space-optimized to .
  • By augmenting the DP table, you can reconstruct the actual sequence of edits, a powerful feature for practical applications.

Preview of the Next Lesson:
With this solid foundation in algorithms, we will now shift our focus up a level of abstraction. In the next module, we will begin our study of software architecture and design by exploring the SOLID principles. This will be a key step in our journey from a developer's mindset to an architect's, learning how to build software that is robust, maintainable, and scalable.