题目
描述
给出两个单词word1和word2,计算出将word1 转换为word2的最少操作次数。
你可进行三种操作:
1.插入一个字符
2.删除一个字符
3.替换一个字符
数据范围
len(word1), len(word2) <= 500len(word1),len(word2)<=500
样例
样例 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’)
分析
解读意义给定两个字符串,三种操作,每次可以选一种操作,最少几次操作可以将一个字符串转化成另一个字符串,判断为双序列型动态规划,先确定最后一步,然后确定转移方程
最后一步
将word1转换成word2最后一步可以选择三种操作,增删换,设f[i][j]为状态,意义为word1前i个字符和word2前j个字符至少需要多少操作次数能转换成相同的字符串,如果最后一步是删除操作次数就是f[i-1][j]+1,如果最后一步是增加操作次数就是f[i][j-1]+1,如果最后一步是替换操作次数为f[i-1][j-1]+1
解释:删除操作word1的长度i-1,增加word2的长度j-1,可能有这样一个疑惑,为什么增加操作时j-1而不是i+1,其实这里不论是对word1还是word2进行操作结果需要的操作次数都是一样的,比如word1=abc word2=ab,对word1删除c是i-1,word1=ab word2=abc,对word1增加c就相当于对word2删除最后一个字符c所以这里用j-1,方便这里的递推,替换操作是i-1和j-1意思就是将最后一个字符替换成相同的字符,相当于同时删掉了word1和word2的最后一个字符。
这里还隐藏了最后一个操作就是没有操作,当这两个字符串最后一个字符相同时,不需要任何的操作
转移方程
在这四种操作中找出需要操作次数最小的操作,f[i][j]=min(f[i][j-1]+1,f[i-1][j]+1,f[i-1][j-1]+1,f[i-1][j-1]||word1[i-1]==word2[j-1])
代码部分
1.初始化
初始化状态,f[n+1][m+1],要在长度的基础上加一是因为要初始化字符串长度0的情况,当一个字符串为空串时,另一个字符串想要转换成目标字符串需要的操作次数就等于当前字符串的长度,例如word1=abc word2=""
这时abc转换成空字符串只需要三次删除操作
int n=word1.size();
int m=word2.size();
vector<vector<int> > f(n+1,vector<int>(m+1));
for(int i=0;i<=n;i++) //初始化第一列
f[i][0]=i;
//当空串与其他字符串相比较时需要操作的次数就等于那个字符串的长度
for(int i =0;i<=m;i++) //初始化第一行
f[0][i]=i;
2.特殊情况
当一个字符串为空字符串时,直接返回另一个字符串的长度即可,长度都为0时也是一样的,不需要操作就是0次操作
if(n==0)
return m;
if(m==0)
return n;
3.动规核心
在三种操作中进行比较,把操作次数最小的情况赋值给当前f[i][j],当两个字符串最后一个字符相同时,还跟它前面的情况直接比较f[i-1][j-1]
for(int i=1;i<=n;i++) //动规核心
for(int j=1;j<=m;j++)
{
f[i][j]=min(f[i-1][j]+1,f[i][j-1]+1);
f[i][j]=min(f[i][j],f[i-1][j-1]+1);
if(word1[i-1]==word2[j-1])
f[i][j]=min(f[i][j],f[i-1][j-1]);
}
完整代码
class Solution {
public:
int minDistance(string &word1, string &word2) {
int n=word1.size();
int m=word2.size();
vector<vector<int> > f(n+1,vector<int>(m+1));
for(int i=0;i<=n;i++) //初始化第一列
f[i][0]=i;
//当空串与其他字符串相比较时需要操作的次数就等于那个字符串的长度
for(int i =0;i<=m;i++) //初始化第一行
f[0][i]=i;
if(n==0)
return m;
if(m==0)
return n;
for(int i=1;i<=n;i++) //动规核心
for(int j=1;j<=m;j++)
{
f[i][j]=min(f[i-1][j]+1,f[i][j-1]+1);
f[i][j]=min(f[i][j],f[i-1][j-1]+1);
if(word1[i-1]==word2[j-1])
f[i][j]=min(f[i][j],f[i-1][j-1]);
}
return f[n][m];
}
};
总结:
双序列型动态规划,状态f[i][j],i和j一般来说就是两个目标字符串的长度,值就是题目中的要求,…最小,…最大,等等,转移方程就是在所有的情况中找出题目中的要求,…最小,…最大,还需要考虑特殊情况直接返回