📌  相关文章
📜  尽量减少使两个字符串相等所需的删除或插入(1)

📅  最后修改于: 2023-12-03 15:09:40.338000             🧑  作者: Mango

尽量减少使两个字符串相等所需的删除或插入

给定两个字符串 S 和 T,需要判断最少需要删除或者插入多少个字符才可以使它们相等。本文将提供两种常见的方法。

动态规划

若将两个字符串变得相等,显然需要知道这两个字符串各前缀的最长公共子序列的长度,具体来说,

  • 如果最后一个字符相等,那么目标字符串删除去这个字符后所需的最小操作次数必然等于不包含这两个字符的两个子序列(即两个字符串各减去最后一个字符)的最小操作次数之和。
  • 如果最后一个字符不相等,那么目标字符串删除与增加必然等价于两个字符串各减去和增加一个字符,例如将 S 删掉一个字符,就等价于将 T 增加一个字符。因此目标字符串操作次数等于删除 S 的最后一个字符所需的最小操作次数与删除 T 的最后一个字符所需的最小操作次数二者的最小值。

结合这两种情况,我们可以列出动态规划方程:

$$dp[i][j]=\begin{cases} 0&i=0,j=0\ dp[i-1][j-1]+1& s_{i}=t_{j}\ \min{dp[i-1][j],dp[i][j-1]}+1& s_{i}\neq t_{j} \end{cases}$$

其中 $dp[i][j]$ 表示字符串 $S$ 前 $i$ 个字符和字符串 $T$ 前 $j$ 个字符的最少操作次数。

def minDistance(s: str, t: str) -> int:
    m, n = len(s), len(t)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(m + 1):
        dp[i][0] = i
    for j in range(n + 1):
        dp[0][j] = j
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s[i - 1] == t[j - 1]:
                dp[i][j] = dp[i - 1][j - 1]
            else:
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1
    return dp[m][n]

复杂度分析:

设 $m=\lvert S\rvert$,$n=\lvert T\rvert$。

  • 时间复杂度:$O(mn)$。
  • 空间复杂度:$O(mn)$。
最长公共子序列

当然,也可以用另一种方式来求出这个最长公共子序列的长度。具体来说,我们可以定义 $dp[i][j]$ 为字符串 $S$ 的前 $i$ 个字符与字符串 $T$ 的前 $j$ 个字符的最长公共子序列的长度,那么最少需要删除或者插入的字符数就是 $m + n - 2\times dp[m][n]$。

为什么呢?因为最长公共子序列把两个字符串各减去了一些字符,所以两个字符串相等所需的删除或者插入次数就是两个字符串长度减去最长公共子序列的长度的两倍。

def minDistance(s: str, t: str) -> int:
    m, n = len(s), len(t)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s[i - 1] == t[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
    return m + n - 2 * dp[m][n]

复杂度分析:

设 $m=\lvert S\rvert$,$n=\lvert T\rvert$。

  • 时间复杂度:$O(mn)$。
  • 空间复杂度:$O(mn)$。