Medium
题目描述
给你两个单词 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由小写英文字母组成
解题思路
这是一道经典的动态规划问题,也被称为编辑距离或莱文斯坦距离问题。
核心思路:
定义 dp[i][j] 表示将 word1 的前 i 个字符转换为 word2 的前 j 个字符所需的最小操作数。
状态转移方程:
初始化:
dp[0][j] = j(空串转换为word2的前j个字符需要j次插入操作)dp[i][0] = i(word1的前i个字符转换为空串需要i次删除操作)
状态转移:
- 如果
word1[i-1] == word2[j-1],则dp[i][j] = dp[i-1][j-1](字符相同,不需要操作) - 否则,
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])dp[i-1][j] + 1:删除word1[i-1]dp[i][j-1] + 1:插入word2[j-1]dp[i-1][j-1] + 1:替换word1[i-1]为word2[j-1]
- 如果
优化方案: 可以使用滚动数组将空间复杂度从 O(mn) 优化到 O(n),但为了清晰理解,推荐使用二维数组的标准解法。
代码实现
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.length(), n = word2.length();
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
// 初始化边界条件
for (int i = 0; i <= m; i++) {
dp[i][0] = i;
}
for (int j = 0; j <= n; j++) {
dp[0][j] = j;
}
// 填充dp表
for (int i = 1; i <= m; i++) {
for (int 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 + min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]});
}
}
}
return dp[m][n];
}
};
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 初始化边界条件
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
# 填充dp表
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
return dp[m][n]
public 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;
}
// 填充dp表
for (int i = 1; i <= m; i++) {
for (int 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(Math.Min(dp[i - 1, j], dp[i, j - 1]), dp[i - 1, j - 1]);
}
}
}
return dp[m, n];
}
}
/**
* @param {string} word1
* @param {string} word2
* @return {number}
*/
var minDistance = function(word1, word2) {
const m = word1.length;
const n = word2.length;
const dp = Array(m + 1).fill().map(() => 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], // delete
dp[i][j - 1], // insert
dp[i - 1][j - 1] // replace
);
}
}
}
return dp[m][n];
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(m×n),其中 m 和 n 分别是两个字符串的长度,需要填充整个 dp 表 |
| 空间复杂度 | O(m×n),使用二维数组存储 dp 状态 |
相关题目
. One Edit Distance (Medium)
. Delete Operation for Two Strings (Medium)
. Uncrossed Lines (Medium)
. Longest Palindrome After Substring Concatenation II (Hard)