给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
示例 1:
输入: word1 = "horse", word2 = "ros"
输出: 3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
示例 2:
输入: word1 = "intention", word2 = "execution"
输出: 5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
提示:
0 <= word1.length, word2.length <= 500word1和word2由小写英文字母组成
🏠 生活案例:乐高拼图改装
想象你手里有一排乐高积木组成的单词 word1(比如 "horse"),你的目标是把它改装成另一个单词 word2(比如 "ros")。
但是,改装是有代价的,每动一动手都要算作“1步”。你只能做三件事:
- 插入:往
word1里塞一个新积木。 - 删除:从
word1里扔掉一个积木。 - 替换:把
word1里的一个积木换成另一种颜色/字母。
题目问的是:最少花多少步能改完?
💻 代码实现与生活化注释
这段代码的核心是建立一个“记账表格”(DP 数组),记录从 word1 的前 个字符变到 word2 的前 个字符最少需要几步。
JavaScript
/**
* @param {string} word1
* @param {string} word2
* @return {number}
*/
var minDistance = function(word1, word2) {
let m = word1.length;
let n = word2.length;
// 1. 建立一个二维“记账表格” dp[m+1][n+1]
// dp[i][j] 表示:把 word1 的前 i 个字变成 word2 的前 j 个字要几步
let dp = Array.from({length: m + 1}, () => Array(n + 1).fill(0));
// 2. 初始化:如果其中一个单词是空的
// word1 有 i 个字,变到空单词,只能全部“删除”,步数就是 i
for (let i = 0; i <= m; i++) dp[i][0] = i;
// 空单词变到 word2 有 j 个字,只能全部“插入”,步数就是 j
for (let j = 0; j <= n; j++) dp[0][j] = j;
// 3. 开始填表(动态规划的核心)
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
// 如果当前的两个字母一模一样(比如都是 's')
// 就像乐高颜色刚好对上了,不需要任何操作!
// 步数直接继承“还没加这两个字母时”的步数
if (word1[i - 1] === word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
// 如果字母不一样,我们需要从三种方案里选个最省钱(步数最少)的:
// dp[i-1][j-1] + 1 : 替换(把这个积木换掉)
// dp[i][j-1] + 1 : 插入(往 word1 塞一个)
// dp[i-1][j] + 1 : 删除(把 word1 现有的扔掉)
dp[i][j] = Math.min(
dp[i - 1][j - 1], // 替换
dp[i][j - 1], // 插入
dp[i - 1][j] // 删除
) + 1; // 别忘了加上当前这一步的操作
}
}
}
// 4. 表格右下角的值就是我们要的最终答案
return dp[m][n];
};
🧩 为什么这样有效?(状态转移图解)
动态规划的魅力在于**“利用已知的答案推算未知的答案”**。
当你计算 dp[i][j] 时,你其实是在看这三个格子:
- 左边
dp[i][j-1]:代表“插入”操作。 - 上边
dp[i-1][j]:代表“删除”操作。 - 左上角
dp[i-1][j-1]:代表“替换”操作。
程序会自动对比这三条路,哪条路走的步数最少,就走哪条路。这就像导航软件会对比“高速、国道、小路”三条线,选一个最短时间到达终点一样。
复杂度分析
- 时间复杂度:。你需要把这个表格里的每一个格子都填满。
- 空间复杂度:。我们开辟了一个二维数组来存步数。
这个算法在拼写纠错、基因序列比对(看两个 DNA 序列有多像)等现实场景中都有大规模的应用!