动态规划
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个而已。