问题
给你两个单词 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’)
思路
编辑距离终于来了,这道题目如果大家没有了解动态规划的话,会感觉超级复杂
编辑距离是用动规来解决的经典题目,这道题目看上去好像很复杂,但用动规可以很巧妙的算出最少编辑距离
确定dp数组以及下标的含义
**dp[i][j]**表示以下标**i-1**为结尾的字符串**word1**,和以下标**j-1**为结尾的字符串**word2**,最近编辑距离为**dp[i][j]**- 这里在强调一下:为啥要表示下标
i-1为结尾的字符串呢,为啥不表示下标i为结尾的字符串呢?用i来表示也可以!但我统一以下标i-1为结尾的字符串,在下面的递归公式中会容易理解一点
确定递推公式
在确定递推公式的时候,首先要考虑清楚编辑的几种操作,整理如下:
if (word1[i - 1] == word2[j - 1])- 不操作
if (word1[i - 1] != word2[j - 1])- 增
- 删
- 换
- 也就是如上四种情况
if (word1[i - 1] == word2[j - 1])那么说明不用任何编辑,dp[i][j]就应该是dp[i - 1][j - 1],即dp[i][j] = dp[i - 1][j - 1];- 那么为什么是
dp[i][j] = dp[i - 1][j - 1]呢? - 那么就在回顾上面讲过的
dp[i][j]的定义,word1[i - 1]与word2[j - 1]相等了,那么就不用编辑了,以下标i-2为结尾的字符串word1和以下标j-2为结尾的字符串word2的最近编辑距离dp[i - 1][j - 1]就是dp[i][j]了
- 那么为什么是
在整个动规的过程中,最为关键就是正确理解dp[i][j]的定义!
if (word1[i - 1] != word2[j - 1]),此时就需要编辑了,如何编辑呢?操作一:
word1增加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-2为结尾的word1与j-1为结尾的word2的最近编辑距离加上一个增加元素的操作- 即
dp[i][j] = dp[i - 1][j] + 1;
- 即
操作二:
word2添加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-1为结尾的word1与j-2为结尾的word2的最近编辑距离加上一个增加元素的操作- 即
dp[i][j] = dp[i][j - 1] + 1;
- 即
这里有同学发现了,怎么都是添加元素,删除元素去哪了
- word2添加一个元素,相当于word1删除一个元素,例如 word1 = “ad” ,word2 = “a”,word2添加一个元素d,也就是相当于word1删除一个元素d,操作数是一样!
操作三:替换元素,
word1替换word1[i - 1],使其与word2[j - 1]相同,此时不用增加元素,那么以下标i-2为结尾的word1与j-2为结尾的word2的最近编辑距离加上一个替换元素的操作- 即
dp[i][j] = dp[i - 1][j - 1] + 1;
- 即
综上,当
if (word1[i - 1] != word2[j - 1])时取最小的,即:dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;if (word1[i - 1] == word2[j - 1]) {dp[i][j] = dp[i - 1][j - 1];}else {dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;}
dp数组如何初始化
**dp[i][j]**表示以下标**i-1**为结尾的字符串**word1**,和以下标**j-1**为结尾的字符串**word2**,最近编辑距离为**dp[i][j]**那么
dp[i][0]和dp[0][j]表示什么呢?dp[i][0]:以下标i-1为结尾的字符串word1,和空字符串word2,最近编辑距离为dp[i][0]- 那么
dp[i][0]就应该是i,对空字符串做添加元素的操作就可以了,即:dp[i][0] = i; - 同理
dp[0][j] = j;for (int i = 0; i <= word1.length(); i++) dp[i][0] = i;for (int j = 0; j <= word2.length(); j++) dp[0][j] = j;
确定遍历顺序
从如下四个递推公式:
dp[i][j] = dp[i - 1][j - 1]dp[i][j] = dp[i - 1][j - 1] + 1dp[i][j] = dp[i][j - 1] + 1dp[i][j] = dp[i - 1][j] + 1
可以看出dp[i][j]是依赖左方,上方和左上方元素的,如图:
所以在dp矩阵中一定是从左到右从上到下去遍历
for (int i = 1; i <= word1.length(); i++) {for (int j = 1; j <= word2.length(); j++) {if (word1[i - 1] == word2[j - 1]) {dp[i][j] = dp[i - 1][j - 1];}else {dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;}}}
- 举例推导dp数组
- 以示例1,输入:word1 = “horse”, word2 = “ros”为例,dp矩阵状态图如下:

class Solution {public int minDistance(String word1, String word2) {int[][] dp = new int[word1.length() + 1][word2.length() + 1];for (int i = 0; i <= word1.length(); i++) dp[i][0] = i;for (int j = 0; j <= word2.length(); j++) dp[0][j] = j;for (int i = 1; i <= word1.length(); i++) {for (int j = 1; j <= word2.length(); 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 - 1][j - 1], dp[i][j - 1]),dp[i - 1][j]) + 1;}}}return dp[word1.length()][word2.length()];}}
