day56 ● 583. 两个字符串的删除操作 ● 72. 编辑距离

85 阅读4分钟

一、问题描述

  1. 两个字符串的删除操作

给定两个单词 word1 和 word2,请找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。

示例:

输入: "sea", "eat" 输出: 2 解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"

  1. 编辑距离

给定两个单词 word1 和 word2,请计算出将 word1 转换成 word2 所使用的最少操作数。

你可以对一个单词进行如下三种操作:

插入一个字符 删除一个字符 替换一个字符

示例:

输入: word1 = "horse", word2 = "ros" 输出: 3 解释: horse -> rorse (将 'h' 替换为 'r') rorse -> rose (删除 'r') rose -> ros (删除 'e')

二、动态规划解法

  1. 两个字符串的删除操作

定义状态:

dp[i][j] 表示 word1 的前 i 个字符和 word2 的前 j 个字符的最少删除次数。

转移方程:

当 word1[i-1] == word2[j-1] 时,dp[i][j] = dp[i-1][j-1];

当 word1[i-1] != word2[j-1] 时,dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1。

初始状态:

dp[0][j] = j,表示将 word2 的前 j 个字符全部删除;

dp[i][0] = i,表示将 word1 的前 i 个字符全部删除。

最终答案:

dp[m][n],其中 m 和 n 分别为 word1 和 word2 的长度。

Java 代码实现:

class Solution {
    public int minDistance(String word1, String word2) {
        int m = word1.length(), n = word2.length();
        int[][] dp = new int[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++) {
                if (word1.charAt(i-1) == word2.charAt(j-1)) {
                    dp[i][j] = dp[i-1][j-1];
                } else {
                    dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + 1;
                }
            }
        }
        return dp[m][n];
    }
}
  1. 编辑距离

定义状态:

dp[i][j] 表示 word1 的前 i 个字符和 word2 的前 j 个字符的最少编辑距离。

转移方程:

当 word1[i-1] == word2[j-1] 时,dp[i][j] = dp[i-1][j-1];

当 word1[i-1] != word2[j-1] 时,dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1。

其中,dp[i-1][j-1] 表示替换操作,dp[i-1][j] 表示删除操作,dp[i][j-1] 表示插入操作。

初始状态:

dp[0][j] = j,表示将 word2 的前 j 个字符全部插入;

dp[i][0] = i,表示将 word1 的前 i 个字符全部删除。

最终答案:

dp[m][n],其中 m 和 n 分别为 word1 和 word2 的长度。

Java 代码实现:

class Solution {
    public int minDistance(String word1, String word2) {
        int m = word1.length(), n = word2.length();
        int[][] dp = new int[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++) {
                if (word1.charAt(i-1) == word2.charAt(j-1)) {
                    dp[i][j] = dp[i-1][j-1];
                } else {
                    dp[i][j] = Math.min(dp[i-1][j-1], Math.min(dp[i-1][j], dp[i][j-1])) + 1;
                }
            }
        }
        return dp[m][n];
    }
}

三、技术报告

动态规划是一种非常重要的算法思想,它已经被广泛应用在各种领域中。动态规划的基本思想是将问题分解为子问题,通过求解子问题来解决原问题,从而避免了重复计算,提高了算法效率。

本文介绍了两个字符串相关的动态规划问题:两个字符串的删除操作和编辑距离。这两个问题都可以通过动态规划来解决,其中最重要的一步是定义状态以及状态转移方程。

在两个字符串的删除操作中,我们定义状态 dp[i][j] 表示 word1 的前 i 个字符和 word2 的前 j 个字符的最少删除次数。当 word1[i-1] == word2[j-1] 时,dp[i][j] = dp[i-1][j-1];当 word1[i-1] != word2[j-1] 时,dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1。初始状态为 dp[0][j] = j 和 dp[i][0] = i,最终答案为 dp[m][n],其中 m 和 n 分别为 word1 和 word2 的长度。

在编辑距离问题中,我们定义状态 dp[i][j] 表示 word1 的前 i 个字符和 word2 的前 j 个字符的最少编辑距离。当 word1[i-1] == word2[j-1] 时,dp[i][j] = dp[i-1][j-1];当 word1[i-1] != word2[j-1] 时,dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1。初始状态和最终答案的定义与两个字符串的删除操作相同。

以上两个问题的动态规划解法都具有时间复杂度 O(mn) 和空间复杂度 O(mn),其中 m 和 n 分别为两个字符串的长度。在实际应用中,我们可以通过优化空间复杂度来进一步提高算法效率。