Edit Distance Problem Levenshtein in DSA Go - Time & Space Complexity
We want to understand how the time needed to find the edit distance between two words grows as the words get longer.
The question is: How does the number of steps change when the input words become bigger?
Analyze the time complexity of the following code snippet.
func min(a, b, c int) int {
if a < b {
if a < c {
return a
}
return c
}
if b < c {
return b
}
return c
}
func EditDistance(s1, s2 string) int {
m, n := len(s1), len(s2)
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
}
for i := 0; i <= m; i++ {
for j := 0; j <= n; j++ {
if i == 0 {
dp[i][j] = j
} else if j == 0 {
dp[i][j] = i
} else if s1[i-1] == s2[j-1] {
dp[i][j] = dp[i-1][j-1]
} else {
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
}
}
}
return dp[m][n]
}
This code calculates the minimum number of edits (insert, delete, replace) to change one string into another using a table.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Nested loops filling a 2D table of size (m+1) x (n+1).
- How many times: Each cell is computed once, so about (m+1) * (n+1) times.
As the lengths of the two strings grow, the number of steps grows roughly by multiplying their lengths.
| Input Size (m, n) | Approx. Operations |
|---|---|
| 10, 10 | 100 |
| 100, 100 | 10,000 |
| 1000, 1000 | 1,000,000 |
Pattern observation: Doubling the length of both strings roughly squares the work needed.
Time Complexity: O(m * n)
This means the time needed grows proportionally to the product of the lengths of the two input strings.
[X] Wrong: "The algorithm runs in linear time because it just compares characters one by one."
[OK] Correct: The algorithm compares all pairs of prefixes, so it fills a table with m times n cells, not just a single pass.
Understanding this time complexity helps you explain how dynamic programming improves over simple recursion and why this approach is efficient for comparing strings.
"What if we used recursion without memoization? How would the time complexity change?"
Practice
Solution
Step 1: Understand the definition of Edit Distance
Edit Distance counts how many changes (insert, delete, replace) are needed to convert one string to another.Step 2: Identify what is measured
It measures the minimum number of single-character edits required.Final Answer:
The minimum number of single-character edits to change one string into the other -> Option CQuick Check:
Edit Distance = minimum edits [OK]
- Confusing edit distance with string length
- Thinking it counts only matching characters
- Assuming it measures length difference only
s1 and s2?Solution
Step 1: Identify input types for strings
The function must accept two strings as input parameters.Step 2: Identify output type
The output is an integer representing the edit distance.Final Answer:
func Levenshtein(s1, s2 string) int -> Option BQuick Check:
Input: strings, Output: int [OK]
- Using int instead of string for inputs
- Returning string instead of int
- Mixing input types incorrectly
"kitten" and "sitting"?
package main
import "fmt"
func main() {
s1 := "kitten"
s2 := "sitting"
dist := Levenshtein(s1, s2)
fmt.Println(dist)
}
func Levenshtein(a, b string) int {
m, n := len(a), len(b)
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
}
for i := 0; i <= m; i++ {
dp[i][0] = i
}
for j := 0; j <= n; j++ {
dp[0][j] = j
}
for i := 1; i <= m; i++ {
for j := 1; j <= n; j++ {
cost := 0
if a[i-1] != b[j-1] {
cost = 1
}
dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+cost)
}
}
return dp[m][n]
}
func min(x, y, z int) int {
if x < y {
if x < z {
return x
}
return z
}
if y < z {
return y
}
return z
}Solution
Step 1: Understand the strings and operations
"kitten" to "sitting" requires substitutions k->s, e->i, plus insert g.Step 2: Calculate minimum edits
Minimum edits are 2 substitutions + 1 insertion = 3 edits total.Final Answer:
3 -> Option DQuick Check:
Levenshtein("kitten", "sitting") = 3 [OK]
- Counting only substitutions
- Ignoring insertions
- Miscounting character positions
func Levenshtein(a, b string) int {
m, n := len(a), len(b)
dp := make([][]int, m)
for i := range dp {
dp[i] = make([]int, n)
}
for i := 0; i < m; i++ {
dp[i][0] = i
}
for j := 0; j < n; j++ {
dp[0][j] = j
}
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
cost := 0
if a[i-1] != b[j-1] {
cost = 1
}
dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+cost)
}
}
return dp[m-1][n-1]
}Solution
Step 1: Check dp slice initialization
dp must have size m+1 by n+1 to include empty string cases, but code uses m and n only.Step 2: Understand impact of wrong sizes
Using m and n causes index out of range errors and incorrect base cases.Final Answer:
dp slice sizes should be m+1 and n+1, not m and n -> Option AQuick Check:
dp size = m+1, n+1 [OK]
- Initializing dp with wrong sizes
- Misplacing cost calculation
- Returning wrong dp cell
s1 = "intention" and s2 = "execution", what is the minimum edit distance using Levenshtein algorithm? Consider insertions, deletions, and substitutions only.Solution
Step 1: Analyze differences between "intention" and "execution"
Compare characters and count edits: substitutions and insertions/deletions as needed.Step 2: Calculate minimal edits
Minimum edits calculated are 6 total.Final Answer:
6 -> Option AQuick Check:
Levenshtein("intention", "execution") = 6 [OK]
- Underestimating substitutions
- Ignoring insertions/deletions
- Confusing character positions
Ready to prove it?
Pass the test and this pattern moves unknown
