Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)

You have the following 3 operations permitted on a word:

a) Insert a character

b) Delete a character

c) Replace a character

听人家说,这是 双序列DP问题. 确实,对我来说,这个题的解法好难理解,即使之前做出了几个dp问题.我怎么可能说自己笨呢!

四个优秀的解释:

http://www.stanford.edu/class/cs124/lec/med.pdf

http://www.cnblogs.com/pandora/archive/2009/12/20/levenshtein_distance.html

http://www.jianshu.com/p/39115986db5a

http://www.dreamxu.com/books/dsa/dp/edit-distance.html

一个分治、dp、贪心的优秀小 book:

http://www.dreamxu.com/books/dsa/dc/subset.html

看了人家很多解释,还是自己想出个例子,自己再顺一遍才能较好的理解.Come on!

假设有 3 种操作:

插入,删除 和 修改.假设它们的 cost 均为 1;

注意有的题目可能它们的 cost 不相同, 比如:

  • The costs of both insertion(插入) and deletion(删除) are same value, that is 1;
  • The cost of substitution(替换) is 2.

咱自己的例子:

说例子前需说明什么是 dp[i, j].

dp[i, j] 称为 s1[0..i] 串到 s2[0..j] 的最小距离. 表示 字符串 s1[0..i] 转变成 s2[0..j] 的最小代价.在我们的题中,也可理解为最小步骤(因为无论啥操作,cost都是1).

这句话当初对我来说并不好理解.为了更容易的让大家理解,举个例子:

本解释将跟随题目要求,cost 均为1.

符号 "*" 代表空字符串.

s1 = "a"
s2 = "b"

现在要把 s1 变成 s2,问:最少的步骤是多少? 显然,这种情况下,凭直觉,肯定是1步,既, 1步 substitution.

此时:

这是要对 s1 做 substitution 操作, 将 a 替换成 b:
dp[i, j] = dp[i-1, j-1] + 1 = dp[0, 0] + 1 = 0 + 1 = 1;
若 s1 的第一个字符 a 和 s2 的第一个字符一样的话: dp[1, 1] = dp[0, 0] = 0, 就不需要替换操作了. * a
^
i=1 * b
^
j=1

dp[i = 1, j = 1] 可以写成 dp[i = 0, j = 0] + 1. 就是 s1[0..1] 的串变成 s2[0..1] 的串可表示成 s1的空串变成s2的空串所需次数 + 1.

空串变空串?那还用变?精神病的做法是 * -> a ->*,这个cost = 2, 而dp里存的是最小次数或叫做最下距离,那么显然 dp[i = 0, j = 0] = 0 (空串变空串?两个空串有什么好变化的,对吧)

但真的只有这一种办法吗?不是的.看下面:

这是 s1由空变为b 步骤数已知的情况下, 再删除a:
dp[i, j] = dp[i-1][j] + 1 = 1 + 1 = 2 * a
^
i-1=0 * b
^
j=1

还有一个情况:

这是 s1="a" ,删除a变成空的步骤数已知的情况下,再在最后面插入一个b:
dp[i, j] = dp[i][j-1] + 1 = dp[1][0] + 1 = 1 + 1 = 2 * a
^
i=1 * b
^
j-1=0

dp[i, j]只与其左上,左,上,有关.分别为 dp[i-1,j-1], dp[i,j-1] and dp[i-1,j].

总结起来步骤是这样的:

  1. m = s1 的长度, n = s2 的长度;
  2. 初始化边界:dp[0][j] = j, dp[i][0] = i,其中i = [0,..,m], j = [0,..,n]. 就是空串变某个串, 或某个串变空串的步骤数,肯定是那个串的长度了;
  3. s1[i - 1] = s2[j - 1], 则dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j] + 1, dp[i][j - 1] + 1)); 这表示若dp[i - 1][j - 1], dp[i - 1][j]+1, dp[i][j - 1]+1 已知, 则由这3种 case所表达的状态 到 dp[i][i]的状态.我们取上述三种状态的最小值赋值给dp[i][j]. 其中dp[i - 1][j - 1]不用加1是因为s1和s2最后一个字符是一样的,当然不用再加1,否则+1(就是修改s1最后字符为s2最后字符,其实说最后字符是不妥当的,我们直接认为当前正在处理s1,s2最后面的那个字符,这么想能使问题简单一些.)
  4. s1[i - 1] != s2[j - 1], 则dp[i][j] = min(dp[i - 1][j - 1] + 1, min(dp[i - 1][j] + 1, dp[i][j - 1] + 1)); 注意,除了dp[i - 1][j - 1] + 1有变化外,其他没变.
  5. 空间复杂度问题:我们可以维护一个(m+1) * (n+1) 的 dp 矩阵,另一种更好的办法是只维护一个 m 或 n 大小的数组.

人家想法,咱的代码:

方法一:

\(O(m*n)\) time, \(O(m*n)\) extra space.

int minDistance(string word1, string word2) {
int m = word1.length(), n = word2.length(); // dp: a (m+1) * (n+1) matrix
vector < vector<int> > dp(m + 1, vector<int>(n + 1, 0)); // fill values in boundary
for (int i = 0; i <= m; i++)
dp[i][0] = i;
for (int j = 0; j <= n; j++)
dp[0][j] = j; // dp state transfer formula
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
if (word1[i - 1] == word2[j - 1])
dp[i][j] = min(dp[i - 1][j - 1],
min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));
else
dp[i][j] = min(dp[i - 1][j - 1] + 1,
min(dp[i - 1][j] + 1, dp[i][j - 1] + 1)); return dp[m][n];
}

方法二:

\(O(m*n)\) time, \(O(m)\) extra space.

墨迹了挺长时间,没写出来.

看人家的吧.https://leetcode.com/problems/edit-distance/discuss/

写本文的时候发现,文字描述起来好费劲,啰里啰嗦,自己写作水平根本不行啊.

72. Edit Distance(困难,确实挺难的,但很经典,双序列DP问题)的更多相关文章

  1. 【Leetcode】72 Edit Distance

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

  2. 刷题72. Edit Distance

    一.题目说明 题目72. Edit Distance,计算将word1转换为word2最少需要的操作.操作包含:插入一个字符,删除一个字符,替换一个字符.本题难度为Hard! 二.我的解答 这个题目一 ...

  3. [LeetCode] 72. Edit Distance 编辑距离

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

  4. 72. Edit Distance

    题目: Given two words word1 and word2, find the minimum number of steps required to convert word1 to w ...

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

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

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

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

  7. 72. Edit Distance *HARD*

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

  8. LeetCode - 72. Edit Distance

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

  9. 【一天一道LeetCode】#72. Edit Distance

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given t ...

随机推荐

  1. Docker学习笔记 - Docker的守护进程

    学习目标:  查看Docker守护进程的运行状态 启动.停止.重启Docker守护进程 Docker守护进程的启动选项 修改和查看Docker守护进程的启动选项 1.# 查看docker运行状态  方 ...

  2. groovy入门(2-1)Groovy的Maven插件安装:Plugin execution not covered by lifecycle configuration

    参考链接:http://www.cnblogs.com/rightmin/p/4945797.html 1.引入groovy的jar包 2.引入groovy编译插件 3.遇到问题 Plugin exe ...

  3. SQL查询语句练习

    最近在学习SQL嘛,所以各个地方找题目来练手,毕竟现在能离得开数据库么? Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C ...

  4. Dapper中条件为In的写法

    今天用Dapper更新是用到了IN写法,园子里找了篇文章这样写到 传统sql in (1,2,3) 用dapper就这样写 conn.Query<Users>("SELECT * ...

  5. Python入门之PyCharm的快捷键与常用设置和扩展(Mac系统)

    1. 快捷键 2 . PyCharm的常用设置和扩展 ------------------------------------------------------------------------- ...

  6. Hibernate(七):*.hbm.xml配置文件中Set三个属性

    背景: 在上一篇文章中实现双向关联时,其中在Customer.java中我们使用了java.util.List<Order>来关联多的Order.其实还有另外一种实现方法:使用java.u ...

  7. 南阳OJ-14-会场安排问题---区间不相交

    题目链接: http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=14 题目描述: 学校的小礼堂每天都会有许多活动,有时间这些活动的计划时间会发生冲突 ...

  8. WPF在在设计模式,使用动态样式

    1.问题分析 WPF有时候要用到主题样式,比如颜色主题(红色.黄色之类的)通常是key相同,而value不同,比如会这么写: Background="{DynamicResource Bac ...

  9. 字典的update方法

    >>> dict = {"name":"zara", "age": 7} >>> dict2 = {&q ...

  10. JS中的递归

      递归基础 递归的概念 在程序中函数直接或间接调用自己 直接调用自己 简介调用自己 跳出结构,有了跳出才有结果 递归的思想 递归的调用,最终还是要转换为自己这个函数 如果有个函数foo,如果他是递归 ...