光看题目是不可能做的出这道题的,两个字符串的最优解问题,一定要想到二维dp。dp[i][j]存的是第一个字符的前i位变到第二个字符串的前j位最少需要几步,因为""变到任意一个另外的字符串,需要的步骤都是另一个字符串的长度,所以先给dp数组赋初值。然后从1遍历到n和m,要等于,所以数组要开大一点。接下来有两种情况
1.word[i-1]==word2[j-1],这样相当于不用做任何操作就匹配上了,它需要的总步长就是dp[i-1][j-1]的大小,只要前面匹配上了最后本来就是匹配的。
2.如果不等于,那么有三种做法,在三种做法中选一个最小的再+1就是dp[i][j]。
最后返回dp[n][m]
class Solution {
public:
int minDistance(string word1, string word2) {
//递归会重复计算,dp是循环,不会重复计算,要初始化
int n = word1.length(), m = word2.length();
if(n == 0 || m == 0) return m+n;
//该dp数组代表的是word1前i个字符变到word2前j个字符需要几次操作
vector<vector<int>> dp(n+1,vector<int>(m+1));
//二维dp数组基本都是这么初始化
for(int i = 0; i <= n; ++i){
dp[i][0] = i;
}
for(int j = 0; j <= m; ++j){
dp[0][j] = j;
}
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= m; ++j){
if(word1[i-1] == word2[j-1]){
//如果1和2最后一位一样,那么就和处理前一位的操作的步数一样
dp[i][j] = dp[i-1][j-1];
}
else{
//这是三种方法,第一种是删去1的一个,第二种是删除2的一个,第三种是1后面添上2的最后
//上述三种方法完成后都需要再+1步才能达到相等
dp[i][j] = min({dp[i-1][j],dp[i][j-1],dp[i-1][j-1]})+1;
}
}
}
return dp[n][m];
}
};
很好很好的动态规划的题目,主要是在动态转移方程中表示出三种操作!!
还有basecase也非常非常重要:dp[0][j]设为j,dp[i][0]设为i,在字符串长度为0时,只有增加新字母这条路
状态定义:dp[i][j]表示从1的前i位变到2的前j位最少几步操作
状态转移方程:如果是abc和bac,如果c相等,只需要对ab和ba进行操作就行了
如果不相等,那么就有三种操作,我们假设全部对1进行操作,
删除操作就是用i-1位去变为j
插入操作就是用i去表示j-1位,因为插进来的那一位肯定和j抵消了
替换操作最简单,i和j抵消,用i-1位表示j-1位,我们取三个操作的最小值并加上1,这个1就是这一步操作
class Solution {
public:
int minDistance(string word1, string word2) {
//三种操作,替换,删除,插入
//dp[i][j]代表从1的前i位到2的前j位最少进行几步操作
//状态转移方程:如果1[i-1]==2[j-1],那么dp[i][j] = dp[i-1][j-1]
//如果不相等,那么就有三种操作,重点就是把替换、删除、插入转换成对应方程
int m = word1.length(), n = word2.length();
//为什么要把数组开大一位,这样就不用判断边界了
vector<vector<int>> dp(m+1,vector<int>(n+1));
//basecase:
//dp[0][j]设为j,dp[i][0]设为i,在字符串长度为0时,只有增加新字母这条路
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[i-1] == word2[j-1]){
//如果是abc和bac,如果c相等,只需要对ab和ba进行操作就行了
dp[i][j] = dp[i-1][j-1];
}
else{
//我们假设删除和插入都是在word1上做操作
dp[i][j] = min({dp[i-1][j],dp[i][j-1],dp[i-1][j-1]})+1;
}
}
}
return dp[m][n];
}
};