Edit Distance Problem Levenshtein in DSA C++ - Time & Space Complexity
We want to understand how the time needed to find the minimum edits between two words grows as the words get longer.
How does the work increase when the input words become bigger?
Analyze the time complexity of the following code snippet.
int editDistance(string s1, string s2) {
int m = s1.size(), n = s2.size();
vector> dp(m + 1, vector(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++) {
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 word into another using a table.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Nested loops filling a 2D table of size m by n.
- How many times: Outer loop runs m times, inner loop runs n times, so total m x n times.
As the lengths of the two words increase, the number of operations grows by multiplying their sizes.
| 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 words roughly quadruples the work needed.
Time Complexity: O(m x n)
This means the time needed grows proportionally to the product of the lengths of the two words.
[X] Wrong: "The algorithm only depends on the longer word length, so it is O(max(m, n))"
[OK] Correct: The algorithm compares every character of one word with every character of the other, so both lengths matter equally.
Understanding this time complexity helps you explain how dynamic programming solves problems efficiently by breaking them into smaller parts.
"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 are needed to make two strings equal.Step 2: Identify allowed operations
Allowed operations are 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
- Ignoring substitution as an allowed operation
- Thinking it measures only length difference
Solution
Step 1: Understand base case meaning
dp[i][0] means converting first i characters of string1 to empty string, so requires i deletions.Step 2: Identify correct initialization
Setting dp[i][0] = i correctly counts deletions needed for empty second string.Final Answer:
for (int i = 0; i <= m; i++) dp[i][0] = i; -> Option AQuick Check:
Base case rows = index count [OK]
- Initializing dp[0][j] incorrectly to zero
- Starting loops from 1 instead of 0
- Setting dp[0][0] to 1 instead of 0
int levenshteinDistance(const string &s, const string &t) {
int m = s.size(), n = t.size();
vector dp(m + 1, vector<int>(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++) {
if (s[i - 1] == t[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];
}
cout << levenshteinDistance("kitten", "sitting") << endl;Solution
Step 1: Understand the strings and operations
"kitten" to "sitting" requires substitutions and insertions.Step 2: Calculate minimum edits
Substitute 'k'->'s', substitute 'e'->'i', insert 'g' at end, total 3 edits.Final Answer:
3 -> Option DQuick Check:
Levenshtein distance("kitten", "sitting") = 3 [OK]
- Counting only substitutions, ignoring insertions
- Misaligning indices causing off-by-one errors
- Confusing Levenshtein with Hamming distance
int levenshteinDistance(string s, string t) {
int m = s.size(), n = t.size();
vector dp(m, vector<int>(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++) {
if (s[i - 1] == t[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];
}Solution
Step 1: Check dp vector initialization
dp must have size (m+1) x (n+1) to include base cases for empty strings.Step 2: Identify impact of wrong size
With size m x n, accessing dp[m][0] or dp[0][n] causes out-of-bounds errors.Final Answer:
The dp vector is initialized with size m x n instead of (m+1) x (n+1) -> Option BQuick Check:
dp size must be (m+1) x (n+1) [OK]
- Forgetting to add +1 to dp dimensions
- Returning dp[m][n] without proper dp size
- Incorrect loop ranges causing index errors
Solution
Step 1: Analyze character differences
Compare "intention" and "execution" character by character, counting edits needed.Step 2: Calculate minimal edits
The optimal alignment requires 6 edits: substitutions, insertions, and deletions to match exactly.Final Answer:
6 -> Option CQuick Check:
Levenshtein distance("intention", "execution") = 6 [OK]
- Ignoring insertions or deletions
- Miscounting substitutions
- Assuming only substitutions needed
Ready to prove it?
Pass the test and this pattern moves unknown
