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 character

Example 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')

这道题让求从一个字符串转变到另一个字符串需要的变换步骤,共有三种变换方式,插入一个字符,删除一个字符,和替换一个字符。题目乍眼一看并不难,但是实际上却暗藏玄机,对于两个字符串的比较,一般都会考虑一下用 HashMap 统计字符出现的频率,但是在这道题却不可以这么做,因为字符串的顺序很重要。还有一种比较常见的错误,就是想当然的认为对于长度不同的两个字符串,长度的差值都是要用插入操作,然后再对应每位字符,不同的地方用修改操作,但是其实这样可能会多用操作,因为删除操作有时同时可以达到修改的效果。比如题目中的例子1,当把 horse 变为 rorse 之后,之后只要删除第二个r,跟最后一个e,就可以变为 ros。实际上只要三步就完成了,因为删除了某个字母后,原来左右不相连的字母现在就连一起了,有可能刚好组成了需要的字符串。所以在比较的时候,要尝试三种操作,因为谁也不知道当前的操作会对后面产生什么样的影响。对于当前比较的两个字符 word1[i] 和 word2[j],若二者相同,一切好说,直接跳到下一个位置。若不相同,有三种处理方法,首先是直接插入一个 word2[j],那么 word2[j] 位置的字符就跳过了,接着比较 word1[i] 和 word2[j+1] 即可。第二个种方法是删除,即将 word1[i] 字符直接删掉,接着比较 word1[i+1] 和 word2[j] 即可。第三种则是将 word1[i] 修改为 word2[j],接着比较 word1[i+1] 和 word[j+1] 即可。分析到这里,就可以直接写出递归的代码,但是很可惜会 Time Limited Exceed,所以必须要优化时间复杂度,需要去掉大量的重复计算,这里使用记忆数组 memo 来保存计算过的状态,从而可以通过 OJ,注意这里的 insertCnt,deleteCnt,replaceCnt 仅仅是表示当前对应的位置分别采用了插入,删除,和替换操作,整体返回的最小距离,后面位置的还是会调用递归返回最小的,参见代码如下:

解法一:

class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size(), n = word2.size();
vector<vector<int>> memo(m, vector<int>(n));
return helper(word1, , word2, , memo);
}
int helper(string& word1, int i, string& word2, int j, vector<vector<int>>& memo) {
if (i == word1.size()) return (int)word2.size() - j;
if (j == word2.size()) return (int)word1.size() - i;
if (memo[i][j] > ) return memo[i][j];
int res = ;
if (word1[i] == word2[j]) {
return helper(word1, i + , word2, j + , memo);
} else {
int insertCnt = helper(word1, i, word2, j + , memo);
int deleteCnt = helper(word1, i + , word2, j, memo);
int replaceCnt = helper(word1, i + , word2, j + , memo);
res = min(insertCnt, min(deleteCnt, replaceCnt)) + ;
}
return memo[i][j] = res;
}
};

根据以往的经验,对于字符串相关的题目且求极值的问题,十有八九都是用动态规划 Dynamic Programming 来解,这道题也不例外。其实解法一的递归加记忆数组的方法也可以看作是 DP 的递归写法。这里需要维护一个二维的数组 dp,其大小为 mxn,m和n分别为 word1 和 word2 的长度。dp[i][j] 表示从 word1 的前i个字符转换到 word2 的前j个字符所需要的步骤。先给这个二维数组 dp 的第一行第一列赋值,这个很简单,因为第一行和第一列对应的总有一个字符串是空串,于是转换步骤完全是另一个字符串的长度。跟以往的 DP 题目类似,难点还是在于找出状态转移方程,可以举个例子来看,比如 word1 是 "bbc",word2 是 "abcd",可以得到 dp 数组如下:

  Ø a b c d
Ø
b
b
c

通过观察可以发现,当 word1[i] == word2[j] 时,dp[i][j] = dp[i - 1][j - 1],其他情况时,dp[i][j] 是其左,左上,上的三个值中的最小值加1,其实这里的左,上,和左上,分别对应的增加,删除,修改操作,具体可以参见解法一种的讲解部分,那么可以得到状态转移方程为:

dp[i][j] =      /    dp[i - 1][j - 1]                                                                   if word1[i - 1] == word2[j - 1]

\    min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1            else

解法二:

class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size(), n = word2.size();
vector<vector<int>> dp(m + , vector<int>(n + ));
for (int i = ; i <= m; ++i) dp[i][] = i;
for (int i = ; i <= n; ++i) dp[][i] = i;
for (int i = ; i <= m; ++i) {
for (int j = ; j <= n; ++j) {
if (word1[i - ] == word2[j - ]) {
dp[i][j] = dp[i - ][j - ];
} else {
dp[i][j] = min(dp[i - ][j - ], min(dp[i - ][j], dp[i][j - ])) + ;
}
}
}
return dp[m][n];
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/72

类似题目:

One Edit Distance

Delete Operation for Two Strings

Minimum ASCII Delete Sum for Two Strings

参考资料:

https://leetcode.com/problems/edit-distance/

https://leetcode.com/problems/edit-distance/discuss/25846/C%2B%2B-O(n)-space-DP

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] 72. Edit Distance 编辑距离的更多相关文章

  1. leetCode 72.Edit Distance (编辑距离) 解题思路和方法

    Edit Distance Given two words word1 and word2, find the minimum number of steps required to convert  ...

  2. [LeetCode] 72. Edit Distance(最短编辑距离)

    传送门 Description Given two words word1 and word2, find the minimum number of steps required to conver ...

  3. 【LeetCode】72. Edit Distance 编辑距离(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 记忆化搜索 动态规划 日期 题目地址:http ...

  4. LeetCode - 72. Edit Distance

    最小编辑距离,动态规划经典题. Given two words word1 and word2, find the minimum number of steps required to conver ...

  5. [leetcode]72. Edit Distance 最少编辑步数

    Given two words word1 and word2, find the minimum number of operations required to convert word1 to ...

  6. 72. Edit Distance(编辑距离 动态规划)

    Given two words word1 and word2, find the minimum number of operations required to convert word1 to  ...

  7. 第十八周 Leetcode 72. Edit Distance(HARD) O(N^2)DP

    Leetcode72 看起来比较棘手的一道题(列DP方程还是要大胆猜想..) DP方程该怎么列呢? dp[i][j]表示字符串a[0....i-1]转化为b[0....j-1]的最少距离 转移方程分三 ...

  8. [leetcode] 72. Edit Distance (hard)

    原题 dp 利用二维数组dp[i][j]存储状态: 从字符串A的0~i位子字符串 到 字符串B的0~j位子字符串,最少需要几步.(每一次删增改都算1步) 所以可得边界状态dp[i][0]=i,dp[0 ...

  9. 【Leetcode】72 Edit Distance

    72. Edit Distance Given two words word1 and word2, find the minimum number of steps required to conv ...

随机推荐

  1. LeetCode707:设计链表 Design Linked List

    爱写bug (ID:iCodeBugs) 设计链表的实现.您可以选择使用单链表或双链表.单链表中的节点应该具有两个属性:val 和 next.val 是当前节点的值,next 是指向下一个节点的指针/ ...

  2. 解决centos ssh连接很慢的问题

    更改配置文件vi /etc/ssh/sshd_config找到UseDNS 将UseDNS前面的#删除,并将YES改为NO,若找不到UseDNS,则手动添加UseDNS,并将其设置成No保存并重启ss ...

  3. 【LOJ#3145】[APIO2019]桥梁(分块,并查集)

    [LOJ#3145][APIO2019]桥梁(分块,并查集) 题面 LOJ 题解 因为某个\(\text{subtask}\)没判\(n=1\)的情况导致我自闭了很久的题目... 如果没有修改操作,可 ...

  4. Spring源码系列 — BeanDefinition扩展点

    前言 前文介绍了Spring Bean的生命周期,也算是XML IOC系列的完结.但是Spring的博大精深,还有很多盲点需要摸索.整合前面的系列文章,从Resource到BeanDefinition ...

  5. 关于5G手机使用4G套餐扫盲

    有些人说换5G手机用4G套餐不用5G套餐可以享受最高 300 mbps 的签约速率.在此我来给你们科普下. 5G套餐分为 500 mbps 和 1000 mbps 两种.且都享受优先接入,顺序是 10 ...

  6. 在进行机器学习建模时,为什么需要验证集(validation set)?

    在进行机器学习建模时,为什么需要评估集(validation set)? 笔者最近有一篇文章被拒了,其中有一位审稿人提到论文中的一个问题:”应该在验证集上面调整参数,而不是在测试集“.笔者有些不明白为 ...

  7. TEXT_CONVERT_XLS_TO_SAP 错误排查

    转自:https://blog.csdn.net/ityangjia/article/details/88827308 本文链接:https://blog.csdn.net/ityangjia/art ...

  8. .NET使用本地outlook客户端发送邮件

    1.添加Microsoft.Office.Interop.Outlook引用 2.封装发送邮件方法 using System; using System.Configuration; using Sy ...

  9. WEBAPI获取数据

    在大学学期期间学习的从mvc中的webapi中取数据 直接看代码 首先是控制器中的 using System; using System.Collections.Generic; using Syst ...

  10. 腾讯WeTest兼容服务再次升级,支持小程序兼容

    WeTest 导读 小程序作为微信内能被便捷地获取和传播的工具,吸引着越来越多的开发者加入其中.无论是小游戏.零售.出行服务还是生活餐饮等,各行各业的小程序出现在用户的手机上,在给用户带来便利的同时, ...