-
Notifications
You must be signed in to change notification settings - Fork 273
Autocorrect Distance
TIP103 Unit 12 Session 1 (Click for link to problem statements)
An autocorrect engine measures how far a typed word word1 is from a dictionary word word2. One edit is an insertion, deletion, or substitution of a single character.
Return the minimum number of edits to turn word1 into word2.
def min_distance(word1, word2):
pass- 💡 Difficulty: Medium
- ⏰ Time to complete: 25-30 mins
- 🛠️ Topics: Dynamic Programming, 2D DP, Edit Distance, Strings
Understand what the interviewer is asking for by using test cases and questions about the problem.
- Established a set (2-3) of test cases to verify their own solution later.
- Established a set (1-2) of edge cases to verify their solution handles complexities.
- Have fully understood the problem and have no clarifying questions.
- Have you verified any Time/Space Constraints for this problem?
- Q: What operations count as a single edit?
- A: Inserting one character, deleting one character, or substituting one character for another. Each counts as exactly one edit.
- Q: Do the edits have to be applied in a particular order or position?
- A: No. We only care about the minimum total number of edits, not the sequence in which they are applied.
- Q: What should the function return if one of the words is empty?
- A: The length of the other word, since every character must be inserted (or deleted) one at a time.
HAPPY CASE
Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation: horse -> rorse (substitute 'h' with 'r'), rorse -> rose (delete 'r'), rose -> ros (delete 'e').
Input: word1 = "intention", word2 = "execution"
Output: 5
Explanation: intention -> inention (delete 't'), inention -> enention (substitute 'i' with 'e'), enention -> exention (substitute 'n' with 'x'), exention -> exection (substitute 'n' with 'c'), exection -> execution (insert 'u').
EDGE CASE
Input: word1 = "", word2 = "abc"
Output: 3
Explanation: The typed word is empty, so all 3 characters of the dictionary word must be inserted.
Input: word1 = "same", word2 = "same"
Output: 0
Explanation: The words are identical, so no edits are needed.
Match what this problem looks like to known categories of problems, e.g. Linked List or Dynamic Programming, and strategies or patterns in those categories.
For String Transformation Problems, we can consider the following approaches:
- Dynamic Programming (2D DP): This is the classic Edit Distance (Levenshtein Distance) problem. The minimum edits for a pair of prefixes depends only on the answers for shorter prefixes, so we can build a 2D table of subproblem answers.
- Recursion with Memoization: Compare the words character by character from the front, branching on insert/delete/substitute and caching results — the top-down version of the same DP.
Plan the solution with appropriate visualizations and pseudocode.
General Idea:
Define a 2D table where dp[i][j] is the minimum number of edits to turn the first i characters of word1 into the first j characters of word2. If the current characters match, no edit is needed and we carry over the diagonal value. Otherwise, we take 1 plus the best of the three possible edits: substitution (diagonal), deletion (up), or insertion (left). The answer is the bottom-right cell.
1) Let m = len(word1) and n = len(word2). Create a (m+1) x (n+1) table `dp`.
2) Fill in the base cases:
a) dp[i][0] = i for every i (turn a prefix of word1 into "" by deleting i characters).
b) dp[0][j] = j for every j (turn "" into a prefix of word2 by inserting j characters).
3) For each i from 1 to m and each j from 1 to n:
a) If word1[i-1] == word2[j-1], set dp[i][j] = dp[i-1][j-1] (no edit needed).
b) Otherwise, set dp[i][j] = 1 + min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1])
corresponding to substitution, deletion, and insertion.
4) Return dp[m][n].
- Forgetting the base cases for the empty string (row 0 and column 0), which anchor the whole table.
- Mixing up the indices:
dp[i][j]describes prefixes of lengthiandj, so the characters being compared areword1[i-1]andword2[j-1]. - Adding 1 to the diagonal even when the characters match, which overcounts edits.
- Confusing which neighbor represents which operation (diagonal = substitution, up = deletion, left = insertion).
Implement the code to solve the algorithm.
def min_distance(word1, word2):
m, n = len(word1), len(word2)
# dp[i][j] = min edits to turn word1[:i] into word2[:j]
dp = [[0] * (n + 1) for _ in range(m + 1)]
# Base cases: transforming to/from an empty string
for i in range(m + 1):
dp[i][0] = i # delete all i characters
for j in range(n + 1):
dp[0][j] = j # insert all j characters
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1] # characters match, no edit
else:
dp[i][j] = 1 + min(
dp[i - 1][j - 1], # substitution
dp[i - 1][j], # deletion from word1
dp[i][j - 1], # insertion into word1
)
return dp[m][n]Review the code by running specific example(s) and recording values (watchlist) of your code's variables along the way.
-
Input: word1 = "horse", word2 = "ros"
- The table is 6 x 4. Row 0 fills as 0,1,2,3 and column 0 fills as 0,1,2,3,4,5.
- At i=3, j=2 ('r' in "hor" vs 'o' — mismatch) and the surrounding cells, the table records that "hor" -> "ro" costs 2.
- The bottom-right cell dp[5][3] resolves to 3: substitute 'h'->'r', delete 'r', delete 'e'.
- Output: 3
-
Input: word1 = "intention", word2 = "execution"
- The table is 10 x 10. The shared suffix "tion" makes the last four diagonal steps free.
- dp[9][9] resolves to 5: delete 't', substitute 'i'->'e', substitute 'n'->'x', substitute 'n'->'c', insert 'u'.
- Output: 5
-
Input: word1 = "", word2 = "abc"
- Only the base-case row applies: dp[0][3] = 3.
- Output: 3
Evaluate the performance of your algorithm and state any strong/weak or future potential work.
Assume M is the length of word1 and N is the length of word2.
-
Time Complexity:
O(M * N)because we fill in every cell of the (M+1) x (N+1) table exactly once, doing constant work per cell. -
Space Complexity:
O(M * N)for the DP table. Since each row depends only on the previous row, this can be reduced toO(min(M, N))by keeping just two rows.