题目
Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.
You have the following 3 operations permitted on a word:
1.Insert a character
2.Delete a character
3.Replace a characterExample 1:
Input: word1 = “horse”, word2 = “ros” Output: 3
Explanation: horse ->rorse (replace ‘h’ with ‘r’) rorse -> rose (remove ‘r’) rose -> ros(remove ‘e’)
Example 2:
Input: word1 = “intention”, word2 = “execution” Output: 5
Explanation:intention -> inention (remove ‘t’) inention -> enention (replace ‘i’ with ‘e’) enention -> exention (replace ‘n’ with ‘x’) exention ->
exection (replace ‘n’ with ‘c’) exection -> execution (insert ‘u’)
这道题目算是hard里面比较简单的一道,也是很经典的动态规划题。
题目大意就是给你俩字符串,然后你每次可以插入、删除、替换一个字符,当然了你也可以什么都不做,然后问你最少需要多少步来完成把第一个串编程第二个串?
分析
很明显,我们在转换整个字符串的过程是建立在转换前面的串的部分的基础上而来的,换句话说就是把一个长度为n的串1转换成一个长度为m的串2肯定是基于前面的转换部分得到的,所以可以转换成更小的子问题,而转换的法则就是题目给出的可以采用的三种方法,然后我们取出这三个方法中操作最小的,保存下来,供后面的部分进行判断。这就是典型的dp。
由于是两个字符串,所以显然是一个二维的DP,不妨设一个二维数组dp[i][j],记串1的长度为l1,串2的长度为l2,数组大小设为
(l1 + 1)行(l2 + 1)列,多出一个位置不仅可以考虑到空串的情况,更方便处理边界。然后dp[i][j]的意义就是把字符串str1中下标0到i-1的部分转换成字符串str2中下标0到j-1的部分所需要的最少操作。那么这样题目所需要的答案就转换成了求dp[l1][l2]
下面来分析如何求dp数组:
首先我们来分析边界情况:
1. 当i和j都是0的时候,也就是串1和串2都是空串的时候,所需要的最少操作显然就是0,什么都不做就匹配了
2. 类似的可以把二维数组的第一行和第一列都写出来,对于第一列第j位的意义,就是把一个空串转换成串2的下标0到j-1的部分,显然最少的操作就是每次加一个字符,比如空串变成a需要一次操作,变成ab需要两次,abc需要三次。。。同理,对于第一行的意义,就是把一个长度为1、2、3…的串变成空串所需要的最少操作,很显然就是都删除对应长度的字符。
下面来考虑一般情况:
对于dp[i][j],我们可以插一个,删除一个,替换一个字符,或者什么都不做。
不妨先来考虑插一个,既然要插一个,肯定是插和str2[j-1]位置相同的元素,那就意味着我们必须先把包括str1的下标i-1之前的部分变成str2下标j - 2之前的部分,然后再在str1插入str2[j - 1]这个字符就能使两者匹配,而这一步操作所需要的最少操作便是dp[i][j-1]所代表的,这样才能保证再插入str2[j-1]之后它俩相同,写成表达式就是
dp[i][j] = dp[i][j - 1] + 1
说完了插,我们还可以删一个,要删除串1的i-1号字符,那就意味着i-2号之前要能和串2的j-1之前匹配,然后删掉i-1号才符合要求,写成表达式就是
dp[i][j] = dp[i - 1][j] + 1
以上两种操作都是针对长度有修改的,还有一种操作是替换字符,这种操作对字符长度没有修改,也就是需要比较dp[i][j]和dp[i - 1][j - 1]的情况,所以只需要考虑i - 1和j - 1位置的字符是否相同,相同就不需要替换,不同就做一次替换,写成表达式就是
dp[i][j] = dp[i - 1][j - 1] (str1[i - 1] == str2[j - 1])
dp[i][j] = dp[i - 1][j - 1] (str[i - 1] != str2[j - 1])
上面分析完了三种操作对一个字符的处理情况,那么由于是要记录最少的操作,所以取这三者当中的最小值作为dp[i][j]的值,分析到这,算法的思想就已经解释完了,只要理解了上面的思路,不难写出下面的代码
参考代码
class Solution {
public:
int minDistance(string word1, string word2) {
int dp[word1.size() + 1][word2.size() + 1];
memset(dp, 0, sizeof(dp));
//edge
dp[0][0] = 0;
for (int i = 1; i < word1.size() + 1; ++i) dp[i][0] = dp[i - 1][0] + 1;
for (int i = 1; i < word2.size() + 1; ++i) dp[0][i] = dp[0][i - 1] + 1;
//
for (int i = 1; i < word1.size() + 1; ++i) {
for (int j = 1; j < word2.size() + 1; ++j) {
//delete one dp[i][j] = dp[i - 1][j] + 1;
//insert one dp[i][j] = dp[i][j - 1] + 1;
//if word1[i - 1] == word2[j - 1] dp[i][j] = dp[i - 1][j - 1]
//if word1[i - 1] != word2[j - 1] dp[i][j] = dp[i - 1][j - 1] + 1 //replace
dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
if (word1[i - 1] == word2[j - 1]) dp[i][j] = min(dp[i][j], dp[i - 1][j - 1]);
else dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
return dp[word1.size()][word2.size()];
}
};