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:
- Insert a character.
- Delete a character.
- 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:
- Insert: We can insert the character
s2[j-1]intos1to match it. Now,s1has been transformed to match up tos2[j-1], so we only need to solve the subproblem fors1[0...i-1]ands2[0...j-2]. The cost is1 + dp[i][j-1]. - Delete: We can delete the character
s1[i-1]froms1. Now, we need to solve the subproblem of transformings1[0...i-2]intos2[0...j-1]. The cost is1 + dp[i-1][j]. - Replace: We can replace
s1[i-1]withs2[j-1]. Now these characters match, so we solve the subproblem fors1[0...i-2]ands2[0...j-2]. The cost is1 + dp[i-1][j-1].
Since we want the minimum number of operations, we take the minimum of these three choices.
Base Cases:
- If
s1is empty (i=0), we must insert alljcharacters ofs2. So,dp[0][j] = j. - If
s2is empty (j=0), we must delete allicharacters ofs1. 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.
- Initialization: We fill the first row and first column based on our base cases.
dp[i][0] = ianddp[0][j] = j. - Iteration: We iterate through the table from
dp[1][1]todp[m][n], filling each celldp[i][j]using the recurrence relation we just defined.

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. Sodp[2][2] = 1 + 0 = 1. (We replace 'A' with 'U').
dp[2][3]isdp("SA", "SUN").1 + min(dp(SA,SU), dp(S,SUN), dp(S,SU)). This will be1 + dp(S,SU)which is1+1=2.dp[3][2]isdp("SAT", "SU").1 + min(dp(SAT,S), dp(SA,SU), dp(SA,S)). This will be1 + dp(SA,SU)which is1+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.