一、题目描述
给你两个单词 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 <= 500
word1
和word2
由小写英文字母组成
二、解题思路
定义一个二维数组 dp
,其中 dp[i][j]
表示将 word1
的前 i
个字符转换为 word2
的前 j
个字符所需要的最少操作数。对于 dp
数组的每一个元素,我们可以根据以下情况来更新它的值:
-
如果
word1[i] == word2[j]
,则dp[i][j] = dp[i-1][j-1]
,因为当前字符匹配,所以不需要任何操作,直接继承前面的结果。 -
如果
word1[i] != word2[j]
,则dp[i][j]
可以通过以下三种操作之一得到:
- 插入一个字符:
dp[i][j] = dp[i][j-1] + 1
,相当于将word2[j]
插入到word1
中。 - 删除一个字符:
dp[i][j] = dp[i-1][j] + 1
,相当于将word1[i]
删除。 - 替换一个字符:
dp[i][j] = dp[i-1][j-1] + 1
,相当于将word1[i]
替换为word2[j]
。 - 在这三种操作中,我们选择操作数最少的一种。
初始化 dp
数组时,dp[0][j]
表示将一个空字符串转换为 word2
的前 j
个字符所需要的操作数,显然为 j
;同理,dp[i][0]
表示将 word1
的前 i
个字符转换为空字符串所需要的操作数,显然为 i
。
三、具体代码
java
class Solution {
public int minDistance(String word1, String word2) {
int m = word1.length();
int n = word2.length();
// dp[i][j] 表示将 word1 的前 i 个字符转换为 word2 的前 j 个字符所需要的最少操作数
int[][] dp = new int[m + 1][n + 1];
// 初始化 dp 数组
for (int i = 0; i <= m; i++) {
dp[i][0] = i;
}
for (int j = 0; j <= n; j++) {
dp[0][j] = j;
}
// 动态规划过程
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
}
}
}
// 返回最终结果
return dp[m][n];
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 我们有一个双层循环,分别遍历字符串 word1 和 word2 的所有字符。
- 外层循环遍历 word1 的每个字符,共有 m 次迭代(其中 m 是 word1 的长度)。
- 内层循环遍历 word2 的每个字符,共有 n 次迭代(其中 n 是 word2 的长度)。
- 在循环的每次迭代中,我们执行的是常数时间的操作,即比较字符、更新动态规划数组。
- 因此,总的时间复杂度是 O(m * n),即两个字符串长度的乘积。
2. 空间复杂度
- 我们定义了一个二维数组 dp,其大小为 (m + 1) * (n + 1),其中 m 和 n 分别是字符串 word1 和 word2 的长度。
- 这个二维数组用于存储从 word1 的前 i 个字符转换到 word2 的前 j 个字符所需的最少操作数。
- 因此,空间复杂度也是 O(m * n),即两个字符串长度的乘积。
五、总结知识点
1. 动态规划(Dynamic Programming, DP):
- 动态规划是一种算法设计技术,用于求解具有重叠子问题和最优子结构特性的复杂问题。
- 它将问题分解为较小的子问题,并存储这些子问题的解,以避免重复计算。
- 在本问题中,dp[i][j] 表示将 word1 的前 i 个字符转换为 word2 的前 j 个字符所需要的最少操作数。
2. 二维数组:
- 二维数组是一种数据结构,用于存储多个相同类型的元素,这些元素以行和列的形式排列。
- 在本问题中,dp 是一个二维数组,用于存储不同子问题的解。
3. 字符串操作:
charAt(int index)
方法用于获取字符串中指定索引处的字符。- 在本问题中,使用
charAt(i - 1)
来获取 word1 和 word2 字符串中的字符,因为数组的索引从 0 开始,而 dp 数组的维度是从 1 开始的。
4. 数学函数:
Math.min()
函数用于在多个值中选择最小值。- 在本问题中,使用
Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1])
来确定在当前字符不匹配时,进行插入、删除或替换操作中的最小操作数。
5. 循环结构:
- 使用了双层循环来遍历 word1 和 word2 的所有字符组合,以填充 dp 数组。
6. 边界条件处理:
- 使用了单独的循环来初始化 dp 数组的边界条件,即当其中一个字符串为空时,转换为另一个字符串所需的操作数。
7. 递推关系:
- 通过比较当前字符是否相等,定义了 dp 数组的递推关系,即如果字符相等,则操作数不变;如果字符不等,则取插入、删除、替换三种操作中的最小操作数加一。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。