作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/


题目地址:https://leetcode.com/problems/edit-distance/description/

题目描述

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

题目大意

给了两个字符串,现在有三种操作,问最少做多少次操作,能使word1变成word2。三种操作是:

  1. 插入一个字符
  2. 删除一个字符
  3. 替换一个字符

解题方法

程序的世界真是其妙无穷。

很多人的解法直接上来就是动态规划,其实少了这个动态规划怎么想出来的过程。动态规划的思路就是 递归 => 记忆化搜索 => 动态规划,一步步提升转化出来的,大家都在讲动态规划,其实少了前两步的思考过程。

我现在详细讲解下递归 => 记忆化搜索 => 动态规划的优化过程。

递归

这个题和最长公共子序列非常相似,需要判断最后的一个字符是否相等:

  • 如果相等,则最后一个字符不用做任何操作,那么只用计算除去最后一个字符外的前面的子串的编辑距离即可。
  • 如果不等,则最后一个字符需要进行替换操作,那么只用计算除去最后一个字符外的前面的子串的编辑距离 ,再 +1(最后一个字符的替换操作),即可把word1变成word2。

图源花花酱:

代码比较简单,需要注意的是初始化的数组大小是 L1 + 1L2 + 1,因为函数的意义是 [0, L1], [0, L2] 区间变成相等的最小操作次数,闭区间。

可以按照上面的思路,进行暴力的求解。但是会超时 TLE。

C++代码如下:

class Solution {
public:
int minDistance(string word1, string word2) {
// cout << "word1: " << word1 << " word2: " << word2 << endl;
int M = word1.size();
int N = word2.size();
if (M == 0) return N;
if (N == 0) return M;
if (word1[M - 1] == word2[N - 1]) {
return minDistance(word1.substr(0, M - 1), word2.substr(0, N - 1));
}
return 1 + min(min(minDistance(word1.substr(0, M - 1), word2),
minDistance(word1, word2.substr(0, N - 1))),
minDistance(word1.substr(0, M - 1), word2.substr(0, N - 1)));
}
};

记忆化搜索

上面的超时的原因是会有重复的计算,同样的一个状态会被不同的分支走多次,因此可以使用记忆化搜索,保存一下走过的状态的结果,如果另外一个分支走到了这个状态,那么可以直接查找之前的计算结果。

Python代码如下:

class Solution(object):
def minDistance(self, word1, word2):
"""
:type word1: str
:type word2: str
:rtype: int
"""
L1, L2 = len(word1), len(word2)
dp = [[-1] * (L2 + 1) for _ in range(L1 + 1)]
return self.getDistance(word1, word2, dp, L1, L2) def getDistance(self, word1, word2, dp, pos1, pos2):
if pos1 == 0: return pos2
if pos2 == 0: return pos1
if dp[pos1][pos2] >= 0: return dp[pos1][pos2] res = 0
if word1[pos1 - 1] == word2[pos2 - 1]:
res = self.getDistance(word1, word2, dp, pos1 - 1, pos2 - 1)
else:
res = min(self.getDistance(word1, word2, dp, pos1 - 1, pos2 - 1),
self.getDistance(word1, word2, dp, pos1, pos2 - 1),
self.getDistance(word1, word2, dp, pos1 - 1, pos2)) + 1
dp[pos1][pos2] = res
return res

C++代码如下:

class Solution {
public:
int minDistance(string word1, string word2) {
const int L1 = word1.size();
const int L2 = word2.size();
dp_ = vector<vector<int>>(L1 + 1, vector<int>(L2 + 1, -1));
return getDistance(word1, word2, L1, L2);
}
private:
vector<vector<int>> dp_;
int getDistance(string& word1, string& word2, int l1, int l2) {
if (l1 == 0) return l2;
if (l2 == 0) return l1;
if (dp_[l1][l2] >= 0) return dp_[l1][l2]; int res = 0;
if (word1[l1 - 1] == word2[l2 - 1])
res = getDistance(word1, word2, l1 - 1, l2 - 1);
else
res = min(getDistance(word1, word2, l1 - 1, l2 - 1),
min(getDistance(word1, word2, l1 - 1, l2),
getDistance(word1, word2, l1, l2 - 1))) + 1;
dp_[l1][l2] = res;
return res;
}
};

动态规划

记忆化搜索是自顶向下的操作,即如果求 word1 和 word2 的编辑距离 需要求除掉最后一个字符外的字符串的 编辑距离,依次递归下去。是个把问题规模逐渐变小的过程。

动态规划是自底向上的操作,即先求出最开始的边界条件,然后一步步添加字符,直到添加成 word1 和 word2 的时候,最后的编辑距离。是个把问题规模逐渐变大的过程。

知道了记忆化搜索之后,很容易改成动态规划。这两者的边界是一样的,只不过从递归转成了循环。

python代码如下:

class Solution(object):
def minDistance(self, word1, word2):
"""
:type word1: str
:type word2: str
:rtype: int
"""
L1, L2 = len(word1), len(word2)
dp = [[0] * (L2 + 1) for _ in range(L1 + 1)]
for i in range(L1 + 1):
dp[i][0] = i
for j in range(L2 + 1):
dp[0][j] = j
for i in range(1, L1 + 1):
for j in range(1, L2 + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1
return dp[L1][L2]

C++代码如下:

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

日期

2018 年 12 月 10 日 —— 又是周一!
2020 年 4 月 6 日 —— 又是周一!

【LeetCode】72. Edit Distance 编辑距离(Python & C++)的更多相关文章

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

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

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

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

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

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

  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. R语言与医学统计图形【3】条形图、误差图

    R语言基础绘图系统 基础图形--条形图.误差图 3.条形图 barplot接收的数据是矩阵而非数据框. data <- sample(c(50:80),5) barplot(data,col=h ...

  2. SSH客户端工具连接Linux(有的也可以连接Windows、mac、iOS等多系统平台)

    要远程操作Linux的话还是得靠SSH工具,一般来说,Linux是打开了默认22端口的SSH的服务端,如果我们要远程它的话,就需要一个SSH客户. 我对一款好用的工具主要需要满足以下几点. (1)连接 ...

  3. 硬盘SSD、HDD和SSHD都是什么意思?哪种类型硬盘最好?

    硬盘分类:(1)HHD 机械硬盘(Mechanical hard disk)(2)SSD 固态硬盘(solid state drive/disk)(3)SSHD 混合硬盘,说白了就是HDD+SSD=S ...

  4. 面对大规模 K8s 集群,这款诊断利器必须要“粉一波”!

    作者|段超 来源|尔达 Erda 公众号 背景 我们是一家做商业软件的公司,从一开始我们就把软件交付流程做的非常标准且简单,所有的软件都是基于我们的企业数字化平台 Erda(现已开源)来交付,底层基于 ...

  5. 系列好文 | Kubernetes 弃用 Docker,我们该何去何从?

    作者 | 张攀(豫哲) 来源 | 尔达 Erda 公众号 导读:Erda 作为一站式云原生 PaaS 平台,现已面向广大开发者完成 70w+ 核心代码全部开源!**在 Erda 开源的同时,我们计划编 ...

  6. Hive(十三)【Hive on Spark 部署搭建】

    Hive on Spark 官网详情:https://cwiki.apache.org//confluence/display/Hive/Hive+on+Spark:+Getting+Started ...

  7. 数仓day01

    1. 该项目适用哪些行业? 主营业务在线上进行的一些公司,比如外卖公司,各类app(比如:下厨房,头条,安居客,斗鱼,每日优鲜,淘宝网等等) 这类公司通常要针对用户的线上访问行为.消费行为.业务操作行 ...

  8. Linux定时任务crontable简介

    Linux下定时执行任务的方法:Linux之crond 服务介绍:https://www.cnblogs.com/liang-io/p/9596294.html http://www.mamicode ...

  9. 【排序算法】——冒泡排序、选择排序、插入排序、Shell排序等排序原理及Java实现

    排序 1.定义: 所谓排序,即是整理文件中的内容,使其按照关键字递增或递减的顺序进行排列. 输入:n个记录,n1,n2--,其对应1的关键字为k1,k2-- 输出:n(i1),n(i2)--,使得k( ...

  10. centos7 自动同步时间

    rm -rf /etc/localtime ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime vim /etc/sysconfig/cloc ...