【leetcode】72. 编辑距离

103 阅读2分钟

leetcode-72.png

动态规划

1. 定义状态

我们定义一个二维数组 dp,其中 dp[i][j] 表示将 word1[0:i] 转换为 word2[0:j] 所需的最小操作数。

2. 状态转移方程

  • 如果 word1[i-1] == word2[j-1],则 dp[i][j] = dp[i-1][j-1],因为不需要进行任何操作。

  • 否则,我们可以进行以下三种操作之一,并选择操作次数最小的那个:

    • 插入:dp[i][j-1] + 1
    • 删除:dp[i-1][j] + 1
    • 替换:dp[i-1][j-1] + 1
    • 因此,状态转移方程为:
dp[i][j] = Math.min(
                dp[i - 1][j] + 1,
                dp[i][j - 1] + 1,
                dp[i - 1][j - 1] + 1
           )

3. 初始化

  • dp[0][0] = 0,因为两个空字符串转换不需要操作。
  • dp[i][0] = i,将 word1[0:i] 转换为空字符串需要删除 i 个字符。
  • dp[0][j] = j,将空字符串转换为 word2[0:j] 需要插入 j 个字符。
var minDistance = function (word1, word2) {
    let len1 = word1.length
    let len2 = word2.length
    let dp = Array.from({ length: len1 + 1 }, () => new Array(len2 + 1).fill(0))
    // 注意这里的初始化需要到len1还有len2的地方终止,包括当前数字
    for (let i = 0; i <= len1; ++i) {
        // delete
        dp[i][0] = i
    }
    for (let j = 0; j <= len2; ++j) {
        // insert
        dp[0][j] = j
    }
    for (let i = 1; i <= len1; ++i) {
        for (let j = 1; j <= len2; ++j) {
            // replace
            if (word1[i - 1] === word2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1]
            } else {
                dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + 1)
            }
        }
    }
    return dp[len1][len2]
};

这里把dp数组具象化一点,可以发现,dp[i][j]的结果是左边上边,以及左上三个值的最小值 + 1,这里与我们的最小路径这一题相似度极高,最小路径仅仅是相邻的左边还有上边的两个值,这一题扩充到了3个而已。