https://leetcode-cn.com/problems/edit-distance/solution/bian-ji-ju-chi-mian-shi-ti-xiang-jie-by-labuladong/ (思路很好,有图很好理解)

动态规划该如何优化

描述

给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

插入一个字符
删除一个字符
替换一个字符
示例 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')

解析

编辑距离可以用来比较二者的相似度。编辑距离越小,说明越相似。

编辑距离问题就是给我们两个字符串 s1 和 s2,只能用三种操作,让我们把 s1 变成 s2,求最少的操作数。需要明确的是,不管是把 s1 变成 s2 还是反过来,结果都是一样的。

解决两个字符串的动态规划问题,一般都是用两个指针 i,j 分别指向两个字符串的最后,然后一步步往前走,缩小问题的规模。

定义数组的含义

由于我们的目的求将 word1 转换成 word2 所使用的最少操作数 。那我们就定义 dp[i] [j]的含义为:当字符串 word1 的长度为 i,字符串 word2 的长度为 j 时,将 word1 转化为 word2 所使用的最少操作次数为 dp[i] [j]。

找出关系数组元素间的关系式

接下来我们就要找 dp[i] [j] 元素之间的关系了,比起其他题,这道题相对比较难找一点,但是,不管多难找,大部分情况下,dp[i] [j] 和 dp[i-1] [j]、dp[i] [j-1]、dp[i-1] [j-1] 肯定存在某种关系。因为我们的目标就是,从规模小的,通过一些操作,推导出规模大的。对于这道题,我们可以对 word1 进行三种操作

插入一个字符
删除一个字符
替换一个字符

由于我们是要让操作的次数最小,所以我们要寻找最佳操作。那么有如下关系式:

一、如果我们 word1[i] 与 word2 [j] 相等,这个时候不需要进行任何操作,显然有 dp[i] [j] = dp[i-1] [j-1]。(别忘了 dp[i] [j] 的含义哈)。

二、如果我们 word1[i] 与 word2 [j] 不相等,这个时候我们就必须进行调整,而调整的操作有 3 种,我们要选择一种。三种操作对应的关系试如下(注意字符串与字符的区别):

(1)、如果把字符 word1[i] 替换成与 word2[j] 相等,则有 dp[i] [j] = dp[i-1] [j-1] + 1;

(2)、如果在字符串 word1末尾插入一个与 word2[j] 相等的字符,则有 dp[i] [j] = dp[i] [j-1] + 1;

(3)、如果把字符 word1[i] 删除,则有 dp[i] [j] = dp[i-1] [j] + 1;

(可以用第一个链接来帮助理解)

那么我们应该选择一种操作,使得 dp[i] [j] 的值最小,显然有

dp[i] [j] = min(dp[i-1] [j-1],dp[i] [j-1],dp[[i-1] [j]]) + 1;

于是,我们的关系式就推出来了。

初始值

当 dp[i] [j] 中,如果 i 或者 j 有一个为 0,那么还能使用关系式吗?答是不能的,因为这个时候把 i - 1 或者 j - 1,就变成负数了,数组就会出问题了,所以我们的初始值是计算出所有的 dp[0] [0….n] 和所有的 dp[0….m] [0]。这个还是非常容易计算的,因为当有一个字符串的长度为 0 时,转化为另外一个字符串,那就只能一直进行插入或者删除操作了。

代码

public int minDistance(String word1, String word2) {
if (word1 == null || word1.length() <= 0) {
return word2.length();
} else if (word2 == null || word2.length() <= 0) {
return word1.length();
}
int length1 = word1.length();
int length2 = word2.length();
int[][] array = new int[length1 + 1][length2 + 1];// +1,是因为数组的length2位置有实际意义,表示length2位置的编辑距离
for (int i = 0; i <= length1; i++) {
array[i][0] = i;
}
for (int i = 0; i <= length2; i++) {
array[0][i] = i;
}
for (int ii = 1; ii <= length1; ii++) {
for (int kk = 1; kk <= length2; kk++) {
if (word1.charAt(ii - 1) == word2.charAt(kk - 1)) {
array[ii][kk] = array[ii - 1][kk - 1];
} else {
array[ii][kk] = Math.min(array[ii - 1][kk - 1], Math.min(array[ii][kk - 1], array[ii - 1][kk])) + 1;
}
}
}
return array[length1][length2];
}

优化:可以和[LeetCode] 62. 不同路径 ☆☆☆(动态规划)的思路一下去优化,因为每次计算只用到2行数据。

public int minDistance(String word1, String word2) {
int n1 = word1.length();
int n2 = word2.length();
int[] dp = new int[n2 + 1];
// dp[0...n2]的初始值
for (int j = 0; j <= n2; j++)
dp[j] = j;
// dp[j] = min(dp[j-1], pre, dp[j]) + 1
for (int i = 1; i <= n1; i++) {
int temp = dp[0]; // 相当于初始化
dp[0] = i;// 第 i 行第 0 列的初始值
for (int j = 1; j <= n2; j++) {
// pre 相当于之前的 dp[i-1][j-1]
int pre = temp;
temp = dp[j];// 在下一次temp被赋值给pre时,相当于dp[i-1][j-1]
// 如果 word1[i] 与 word2[j] 相等。第 i 个字符对应下标是 i-1
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[j] = pre;
} else {
dp[j] = Math.min(Math.min(dp[j - 1], pre), dp[j]) + 1;
}
}
}
return dp[n2];
}

[LeetCode] 72. 编辑距离 ☆☆☆☆☆(动态规划)的更多相关文章

  1. [leetcode] 72. 编辑距离(二维动态规划)

    72. 编辑距离 再次验证leetcode的评判机有问题啊!同样的代码,第一次提交超时,第二次提交就通过了! 此题用动态规划解决. 这题一开始还真难到我了,琢磨半天没有思路.于是乎去了网上喵了下题解看 ...

  2. Java实现 LeetCode 72 编辑距离

    72. 编辑距离 给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 . 你可以对一个单词进行如下三种操作: 插入一个字符 删除一个字符 替换一个字 ...

  3. [LeetCode]72. 编辑距离(DP)

    题目 给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 . 你可以对一个单词进行如下三种操作: 插入一个字符 删除一个字符 替换一个字符 示例 1 ...

  4. [Leetcode 72]编辑距离 Edit Distance

    [题目] Given two words word1 and word2, find the minimum number of operations required to convert word ...

  5. leetcode 72 编辑距离 JAVA

    题目: 给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 . 你可以对一个单词进行如下三种操作: 插入一个字符 删除一个字符 替换一个字符 示例  ...

  6. 第30章 LeetCode 72 编辑距离

    每日一句 A flower cannot blossom without sunshine, and man cannot live without love. 花没有阳光就不能盛开,人没有爱就不能生 ...

  7. leetcode 72. 编辑距离

    /***** 定义状态: DP[i][j]其中i表示word1前i个字符,j表示Word2前i个字符 DP[i][j]表示单词1前i个字符匹配单词2前j个字符,最少变换次数: 状态转移: for i: ...

  8. leetcode 72.编辑距离(dp)

    链接:https://leetcode-cn.com/problems/edit-distance/submissions/ 设dp[i][j]表示串s1前i个字符变换成串s2前j个字符所需要的最小操 ...

  9. Leetcode之动态规划(DP)专题-72. 编辑距离(Edit Distance)

    Leetcode之动态规划(DP)专题-72. 编辑距离(Edit Distance) 给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 . 你可 ...

随机推荐

  1. 使用lombok的@Builder的注解的一个坑

    一开发说项目报错 java.lang.Long,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lan ...

  2. java里的数组和list分别在什么情况下使用?

    数组长度固定,List未限定长度,且支持的功能更多,最常用的ArrayList底层实际上也是使用数组实现. 不需要复杂功能和确定长度的情况下,使用数组效率更高,通常情况建议使用List.

  3. aardio 文档

    aardio 文档 根据官方帮助手册制作了一份文档,添加了一些特性. 支持手机阅读 不用电脑也可以学习 aau 了,不受屏幕大小限制,你的小清新还是你的小清新~ 简单的搜索功能 快捷复制示例代码 基于 ...

  4. Qt开发经验小技巧21-30

    如果出现Z-order assignment: is not a valid widget.错误提示,用记事本打开对应的ui文件,找到为空的地方,删除即可. 善于利用QComboBox的addItem ...

  5. [LeetCode] 534. Design TinyURL 设计短网址

    Note: For the coding companion problem, please see: Encode and Decode TinyURL. How would you design ...

  6. 【视频开发】【电子电路技术】监控球机PTZ的功能介绍

    主要分模拟球机和网络球机两种 1.模拟球机 模拟球机除了需要接电源外,还需要接视频线和485控制线才能实现视频传输和云台控制,并且需要配置波特率,地址位和协议. 升级版:HDTVI球机 ① 同轴视控. ...

  7. Java项目main方法启动的两种方式

    1.打包时指定了主类,可以直接用java -jar xxx.jar. <!--main方法打包jar包插件--> <plugin> <artifactId>mave ...

  8. openstack-keystone外组件命令行

    摘自openstack文档 镜像(glance) 列出您可以访问的镜像 $ openstack image list 删除指定的镜像 $ openstack image delete IMAGE 描述 ...

  9. K8S从入门到放弃系列-(15)Kubernetes集群Ingress部署

    Ingress是kubernetes集群对外提供服务的一种方式.ingress部署相对比较简单,官方把相关资源配置文件,都已经集合到一个yml文件中(mandatory.yaml),镜像地址也修改为q ...

  10. FZU2018级算法第一次作业 1.1fibonacci (矩阵快速幂)

    题目 Winder最近在学习fibonacci 数列的相关知识.我们都知道fibonacci数列的递推公式是F(n)=F(n-1)+F(n-2)(n>=2 且n 为整数). Winder想知道的 ...