LeetCode #583 Medium

Delete Operation for Two Strings

Delete Operation for Two Strings: minimum deletions from two strings to make them equal.

Constraints
  • 1 <= word1.length, word2.length <= 500
  • word1 and word2 consist of only lowercase English letters.
dynamic-programmingstringslcs
Open on LeetCode ↗
02

Intuition

Delete operation for two strings finds the minimum number of character deletions, from either string, needed to make them equal. Deletions are the only permitted operation. Thinking about which characters to delete is the hard way round. The characters that survive are far more informative: whatever remains must appear in both strings in the same relative order, which is precisely a common subsequence. Deleting as little as possible means keeping as much as possible, so: - Find the longest common subsequence, then delete everything else — the answer is m + n − 2 × LCS. The factor of 2 catches people out. The LCS is retained in both strings, so it must be subtracted from each length independently. Subtracting it once counts the shared portion as saved from only one string. The LCS itself is the standard DP. When characters match, dp[i][j] = dp[i-1][j-1] + 1 — both are consumed and the match extends the subsequence. When they differ, dp[i][j] = max(dp[i-1][j], dp[i][j-1]), skipping one character from either string and keeping the better result. The base row and column are 0, since a subsequence shared with an empty string has no length. An alternative computes the deletion count directly, with dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1 on a mismatch. It is equally valid, but routing through LCS reuses a pattern that solves many problems rather than a formula specific to this one.

How to spot this pattern

Deletions only, so whatever survives must appear in both strings in order — that's the longest common subsequence. The answer is m + n - 2·lcs: everything outside the LCS gets deleted from one side or the other.

03

Approach

Try it first

Before reading on: price up what the direct approach costs here, then ask what a single cell should mean, and which earlier cells it needs. Aim for O(m*n) time and O(m*n) space.

1

Think about what survives

Rather than choosing deletions, ask what remains. The surviving characters must appear in both strings in the same order — that is a common subsequence by definition.

2

Maximise what is kept

Minimum deletions means maximum retention, so the goal is the longest common subsequence. This converts the problem into a well-known one.

3

Build the LCS table

dp[i][j] is the LCS length of the first i and j characters. Base row and column are 0, since nothing is shared with an empty string.

4

Extend on a character match

When characters match, dp[i][j] = dp[i-1][j-1] + 1 — both are consumed and the match lengthens the subsequence.

5

Take the better skip on a mismatch

When they differ, dp[i][j] = max(dp[i-1][j], dp[i][j-1]), skipping a character from one string or the other and keeping the larger result.

6

Apply the formula with the factor of two

The answer is m + n - 2 * LCS. The subsequence is kept in both strings, so it must be subtracted from each length — subtracting once is the classic error.

7

Cost of the tabulation

Every cell is filled once with O(1) work, giving O(m · n) time and O(m · n) space, reducible to O(min(m, n)) with a rolling row.

04

Solution & live demo

▶1class Solution:
▶2 def minDistance(self, word1:
▶3 str, word2: str) -> int:
▶4 m, n = len(word1), len(word2)
▶5 dp = [[0] * (n + 1) for _ in range(m + 1)]
▶6 for i in range(1, m + 1):
▶7 for j in range(1, n + 1):
▶8 if word1[i - 1] == word2[j - 1]:
▶9 dp[i][j] = dp[i - 1][j - 1] + 1
▶10 else:
▶11 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
▶12 lcs = dp[m][n]
▶13 return m + n - 2 * lcs
05

Common pitfalls

Subtracting the LCS once

✗ Wrong
return m + n - lcs
✓ Right
return m + n - 2 * lcs

The common subsequence is preserved in both strings, so it must be excluded from both deletion counts. Subtracting once leaves the LCS length counted as deletions on one side.

Using edit distance

✗ Wrong
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
✓ Right
dp[i][j] = max(dp[i-1][j], dp[i][j-1])

Edit distance allows substitution and insertion, which this problem forbids — only deletions are permitted. The LCS recurrence takes a max rather than a min and has no diagonal-replace case.

Comparing with the wrong index offset

✗ Wrong
if word1[i] == word2[j]:
✓ Right
if word1[i - 1] == word2[j - 1]:

The table is 1-indexed so that row 0 and column 0 hold the empty-prefix base cases. Reading the strings at i rather than i-1 compares the wrong characters and runs off the end.

06

Edge cases

One string empty

LCS is 0, so the answer is simply the length of the non-empty string, matching that every character in it must be deleted.

Identical strings

LCS equals the full length of either string, giving 0 deletions.

No characters in common at all

LCS is 0, so the answer is len(word1) + len(word2), meaning both strings get fully deleted.

One string a subsequence of the other

LCS equals the shorter string's length, so only the longer string's extra characters need deleting.

07

Complexity

Time
O(m*n)
Space
O(m*n)
Standard LCS table; reducible to O(min(m,n)) with row rolling.