Edit Distance Problem Levenshtein in DSA C - 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.
How does the number of steps change when the input words become bigger?
Analyze the time complexity of the following code snippet.
int min(int a, int b, int c) {
int m = a < b ? a : b;
return m < c ? m : c;
}
int editDistance(char *s1, char *s2, int m, int n) {
if (m == 0) return n;
if (n == 0) return m;
if (s1[m - 1] == s2[n - 1])
return editDistance(s1, s2, m - 1, n - 1);
return 1 + min(
editDistance(s1, s2, m, n - 1), // Insert
editDistance(s1, s2, m - 1, n), // Remove
editDistance(s1, s2, m - 1, n - 1) // Replace
);
}
This code finds the minimum number of edits (insert, remove, replace) to change one string into another using recursion.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls that explore all combinations of substring lengths.
- How many times: Each call branches into three more calls unless base case is reached, leading to many repeated calls.
Each character in the first word can match with each character in the second word, and the recursion tries all possibilities.
| Input Size (m, n) | Approx. Operations |
|---|---|
| 5, 5 | Hundreds of calls |
| 10, 10 | Thousands of calls |
| 20, 20 | Millions of calls |
Pattern observation: The number of calls grows very fast, roughly tripling with each increase in input size.
Time Complexity: O(3^{min(m,n)})
This means the time needed grows very fast as the lengths of the two strings increase, because the recursion explores many overlapping cases.
[X] Wrong: "The recursive solution runs quickly because it only checks each character once."
[OK] Correct: The recursion repeats many calls for the same substrings, causing exponential growth in steps.
Understanding this complexity helps you explain why a simple recursive solution is slow and why dynamic programming is needed to improve it.
"What if we add memoization to store results of subproblems? How would the time complexity change?"
Practice
Solution
Step 1: Understand the definition of Edit Distance
Edit Distance counts the smallest number of changes needed to turn one string into another.Step 2: Identify allowed operations
These operations include insertion, deletion, and substitution of characters.Final Answer:
The minimum number of insertions, deletions, or substitutions to convert one string into another -> Option AQuick Check:
Edit Distance = minimum edits needed [OK]
- Confusing edit distance with matching characters count
- Thinking it only counts substitutions
- Assuming it measures length difference only
Solution
Step 1: Identify the DP table dimensions
The DP table has dimensions (m+1) x (n+1), where m and n are string lengths.Step 2: Initialize first row correctly
The first row dp[0][j] represents converting empty string to prefix of second string, so dp[0][j] = j.Final Answer:
for (int j = 0; j <= n; j++) dp[0][j] = j; -> Option AQuick Check:
First row = 0 to n [OK]
- Mixing row and column indices
- Starting loop from 1 instead of 0
- Initializing first column instead of first row
int min(int a, int b, int c) {
int m = a < b ? a : b;
return m < c ? m : c;
}
int levenshtein(char *s, char *t) {
int m = strlen(s), n = strlen(t);
int dp[m+1][n+1];
for (int i = 0; i <= m; i++) dp[i][0] = i;
for (int j = 0; j <= n; j++) dp[0][j] = j;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
int cost = (s[i-1] == t[j-1]) ? 0 : 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];
}
printf("%d", levenshtein("kitten", "sitting"));Solution
Step 1: Understand the strings and operations
"kitten" to "sitting" requires substitutions and insertions: k->s, e->i, add g.Step 2: Calculate minimum edits
Minimum edits are 3 substitutions and 1 insertion = 4 total edits.Final Answer:
4 -> Option BQuick Check:
Levenshtein("kitten", "sitting") = 4 [OK]
- Counting only substitutions, missing insertions
- Confusing character positions
- Off-by-one errors in indexing
int levenshtein(char *s, char *t) {
int m = strlen(s), n = strlen(t);
int dp[m][n];
for (int i = 0; i <= m; i++) dp[i][0] = i;
for (int j = 0; j <= n; j++) dp[0][j] = j;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
int cost = (s[i-1] == t[j-1]) ? 0 : 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];
}Solution
Step 1: Check dp array declaration
dp is declared as dp[m][n], but indices go up to m and n inclusive, so size is insufficient.Step 2: Understand indexing needs
We need dp of size (m+1) x (n+1) to store base cases and full computation.Final Answer:
dp array size should be (m+1) x (n+1), not m x n -> Option CQuick Check:
dp size = (m+1)*(n+1) [OK]
- Declaring dp too small causing out-of-bounds errors
- Misunderstanding loop ranges
- Incorrect return index
Solution
Step 1: Analyze string lengths and differences
"intention" and "execution" differ in several characters and positions.Step 2: Calculate minimum edits stepwise
Edits include substitutions (i->e, n->x, t->e, e->c, n->u, etc.) totaling 6 changes.Final Answer:
6 -> Option DQuick Check:
Levenshtein("intention", "execution") = 6 [OK]
- Underestimating substitutions needed
- Ignoring insertions or deletions
- Confusing similar characters as matches
Ready to prove it?
Pass the test and this pattern moves unknown
