第六章:编辑距离问题(Edit Distance)——最少操作将字符串转换为目标
一、题目描述
给定两个字符串
word1
和word2
,返回将word1
转换成word2
所使用的最少操作数。
你可以对一个字符串进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
示例:
输入:word1 = "horse"
, word2 = "ros"
输出:3
解释:horse → rorse(替换 h)→ rose(删除 r)→ ros(删除 e)
二、状态定义
令 dp[i][j]
表示将 word1
的前 i
个字符转换为 word2
的前 j
个字符所需要的最少操作数。
三、状态转移方程
我们考虑 word1[i - 1]
和 word2[j - 1]
是否相等:
- 如果相等:
dp[i][j] = dp[i - 1][j - 1];
- 如果不等,考虑三种操作的最小值:
dp[i][j] =
1 +
min(
dp[i - 1][j], // 删除 word1[i - 1]
dp[i][j - 1], // 插入 word2[j - 1]
dp[i - 1][j - 1] // 替换 word1[i - 1] 为 word2[j - 1]
);
四、初始值
dp[0][j] = j
:将空串变成长度为 j 的字符串,需要插入 j 次。dp[i][0] = i
:将长度为 i 的字符串变成空串,需要删除 i 次。
五、实现代码
function minDistance(word1, word2) {
const m = word1.length;
const n = word2.length;
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
for (let i = 0; i <= m; i++) dp[i][0] = i;
for (let j = 0; j <= n; j++) dp[0][j] = j;
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (word1[i - 1] === word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] =
1 +
Math.min(
dp[i - 1][j], // 删除
dp[i][j - 1], // 插入
dp[i - 1][j - 1] // 替换
);
}
}
}
return dp[m][n];
}
六、示例演算
以 word1 = "horse"
, word2 = "ros"
为例:
i \ j | r | o | s | |
---|---|---|---|---|
0 | 1 | 2 | 3 | |
h | 1 | 1 | 2 | 3 |
o | 2 | 2 | 1 | 2 |
r | 3 | 2 | 2 | 2 |
s | 4 | 3 | 3 | 2 |
e | 5 | 4 | 4 | 3 |
最终答案:dp[5][3] = 3
七、小结
“编辑距离”问题是典型的二维动态规划问题,考察的是两个字符串之间的相似度计算。
我们掌握了:
- 如何构造二维状态矩阵
- 如何根据操作定义转移方程
- 如何初始化边界条件
下一章我们将继续探索字符串相关的动态规划问题,比如“最长公共子序列(LCS)”,进一步加深对二维 DP 的理解。