leetCode 72. 编辑距离 动态规划 + 滚动数组 + 优化空间

news2025/2/1 17:03:09

72. 编辑距离 - 力扣(LeetCode)

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。你可以对一个单词进行如下三种操作:

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

编辑距离的应用场景:DNA的编辑,大段大段的DNA,找到几个关键的节点,做插入做替换,可能就能得到目标DNA了

>>思考和分析:

首先,对这个问题做一个定义,然后把这个问题进行分解。怎么分解呢?考虑它的最后一步,此题我们就考虑S1和S2的末尾字符是否相同,相同的话,我该怎么处理?不相同的话,插入删除替换这三种操作又会进入到什么问题思考? 

1.S1和S2的末尾字符相同,意味着可以剥离出去相同的如图蓝色的线表示将x剥离出去。那么剩下要考虑的是S1的前i-1个字符如何编辑才能得到S2的前j-1个字符的问题(O_O)? 

if(S1[i-1] == S2[j-1])
        dp[i][j] = dp[i-1][j-1];

2.S1和S2的末尾字符是不相同的,到底是插入,是删除,还是替换 

问题思考(O_O)?:如果我要进行插入操作,我是对谁进行操作? 

其实是对S1操作,也只能对S1进行操作,也就是我认为S2的末尾是yS1的末尾不是y选择把y给插到S1的末尾。因为可能 S1的这 i 个字符 S2的 j-1 个字符 其实是具有高度的相似性的就是S1末尾缺了一个y。所以我选择插入

dp[i][j] = dp[i][j-1]+1;

若执行删除操作,也只能对S1操作,我把S1的末尾给删掉,意味着 S1的前i-1个字符S2的前j个字符 是具有高度的相似性的就是仅仅因为S1的末尾多了个x,所以我把x删掉

dp[i][j] = dp[i-1][j]+1;

若执行替换操作,可能出现的场景。那可能是认为 S1的前i-1个字符 S2的前j-1个字符 具有高度的相似性仅仅是S1的末尾字符是x,S2的末尾字符是y,只要把这个x换成y,就可以达到最优解!(一定要强调是对S1进行的操作)

dp[i][j] = dp[i-1][j-1]+1;

综上,可以得出这样的递推式:

if(S1[i-1] == S2[j-1])
    dp[i][j] = dp[i-1][j-1];
else
    dp[i][j] = min(dp[i][j-1]+1,dp[i-1][j]+1,dp[i-1][j-1]+1);

问题思考(O_O)?:我到底是进行插入、删除还是替换操作呢?比如说背包问题,我这件物品到底是放还是不放呢?其实对于算法而言,它当时并不知道,只知道按照这个方式进行分解就可以了,总之就是这么两种方式分解,没有什么别的选择方案!对于此题也是一样,要么插入、要么删除、要么替换,在这三种方案里面选一个,编辑距离选一个代价最小的,就可以了!

从上图的左边表格,我们可以看到最终结果为3,也就是最少编辑次数为3。仔细看图中我做的标记,字符串S1="sun" 和 S2="satur",一开始都有相同的 's',所以不做编辑操作,接着执行两次插入操作。有相同的 'u',所以不用做编辑操作。最后将 n 替换 r。可以模拟实现下图的操作效果:

 伪代码:

int edit_ distance(S1,m,S2,n) {
    d[][] = {0};
    for(i=1;i<=m;i++) dp[i][0] = i;
    for(j=1;j<=n;j++) dp[0][j] = j;
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
            if(S1[i-1] == S2[j-1]) dp[i][j] = dp[i-1][j-1];
            else dp[i][j] = 1 + min(dp[i][j-1],
                                    dp[i-1][j],
                                    dp[i-1][j-1]);
    return dp[m][n];
}
// 时间复杂度:O(m*n)

问题思考(O_O)?:有些场景应用里,会要求插入、删除、替换的编辑代价是不一样的,那现实生活中也确实会出现这样的情况,比如插入方便一点,替换麻烦一点,那个时候我们代码如何改呢?只要把这个1改一下,不要写1,把编辑代价加到对应的操作上即可!

(1)二维dp 

  • dp[i][j]:S1的前i个字符 转换成 S2的前j个字符所使用的最少操作数
  • dp[i][j]:表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j] (代码随想录Carl老师给的定义)
class Solution {
public:    
    // 动态规划 二维dp
    int minDistance(string word1, string word2) {
        vector<vector<int>> dp(word1.size()+1,vector<int>(word2.size()+1,0));
        for(int i=1;i<=word1.size();++i) dp[i][0] = i;
        for(int j=1;j<=word2.size();++j) dp[0][j] = j;
        for(int i=1;i<=word1.size();++i) {
            for(int j=1;j<=word2.size();++j) {
                if(word1[i-1] == word2[j-1]) dp[i][j] = dp[i-1][j-1];
                // else dp[i][j] = min({dp[i][j-1]+1,dp[i-1][j]+1,dp[i-1][j-1]+1});
                else dp[i][j] = min({dp[i][j-1],dp[i-1][j],dp[i-1][j-1]})+1;
            }
        }
        return dp[word1.size()][word2.size()];
    }
};
  • 时间复杂度: O(n * m) ,n表示word1的单词长度,m表示word2的单词长度
  • 空间复杂度: O(n * m)

(2)二维dp  优化空间复杂度

class Solution {
public:
    int minDistance(string word1, string word2) {
        vector<vector<int>> dp(2,vector<int>(word2.size()+1,0));
        for(int j=1;j<=word2.size();++j) dp[0][j] = j;
        for(int i=1;i<=word1.size();++i) {
            dp[i%2][0] = i;
            for(int j=1;j<=word2.size();++j) {
                if(word1[i-1] == word2[j-1]) dp[i%2][j] = dp[(i-1)%2][j-1];
                else dp[i%2][j] = min({dp[i%2][j-1],dp[(i-1)%2][j],dp[(i-1)%2][j-1]})+1;
            }
        }
        return dp[word1.size() % 2][word2.size()];
    }
};
  • 时间复杂度: O(n * m) ,n表示word1的单词长度,m表示word2的单词长度
  • 空间复杂度: O(m)

(3)一维dp 优化空间复杂度

class Solution {
public:
    // 动态规划 一维dp 优化空间复杂度
    int minDistance(string word1, string word2) {
        vector<int> dp(word2.size()+1,0);
        for(int j=1;j<=word2.size();++j) dp[j] = j;
        for(int i=1;i<=word1.size();++i) {
            int pre = dp[0];
            dp[0] = i;
            for(int j=1;j<=word2.size();++j) {
                int tmp = dp[j];
                if(word1[i-1] == word2[j-1]) dp[j] = pre;
                else dp[j] = min({dp[j-1],dp[j],pre})+1;
                pre = tmp;
            }
        }
        return dp[word2.size()];
    }
};
  • 时间复杂度: O(n * m) ,n表示word1的单词长度,m表示word2的单词长度
  • 空间复杂度: O(m)

图文参考b站视频和文章:

[轻松掌握动态规划]6.编辑距离_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1sA411B73r/?spm_id_from=333.880.my_history.page.click

代码随想录 (programmercarl.com)icon-default.png?t=N7T8https://www.programmercarl.com/0072.%E7%BC%96%E8%BE%91%E8%B7%9D%E7%A6%BB.html#%E6%80%9D%E8%B7%AF 动态规划终极绝杀! LeetCode:72.编辑距离_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1qv4y1q78f/?spm_id_from=333.788&vd_source=a934d7fc6f47698a29dac90a922ba5a3

来自b站代码随想录的课堂截图:

来自b站邋遢大哥 233 的课堂视频截图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1093743.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

200多万开发者才开发91款应用,国产手机操作系统离开安卓活不了?

随着某国产手机操作系统强调将不再兼容安卓&#xff0c;他们沸腾了&#xff0c;表示将真正独立自主发展&#xff0c;然而业界人士却指出号称拥有220万开发者的该款操作系统至今仅开发了91款应用&#xff0c;彻底撕下了它的遮羞布。 应用对一个智能操作系统有多重要&#xff0c;…

Go 代码块与作用域,变量遮蔽问题详解

Go 代码块与作用域详解 文章目录 Go 代码块与作用域详解一、引入二、代码块 (Block)2.1 代码块介绍2.2 显式代码块2.3 隐式代码块2.4 空代码块2.5 支持嵌套代码块 三、作用域 (Scope)3.1 作用域介绍3.2 作用域划定原则3.3 标识符的作用域范围3.3.1 预定义标识符作用域3.3.2 包代…

互联网Java工程师面试题·Java 并发编程篇·第八弹

目录 33、Java 死锁以及如何避免&#xff1f; 34、死锁的原因 35、怎么唤醒一个阻塞的线程 36、不可变对象对多线程有什么帮助 37、什么是多线程的上下文切换 38、如果你提交任务时&#xff0c;线程池队列已满&#xff0c;这时会发生什么这里区分一下&#xff1a; 39、J…

探索JDK8新特性,Stream 流:构建流的多种方式

当我们处理集合数据时&#xff0c;往往需要对其进行各种操作&#xff0c;如过滤、映射、排序、归约等。在 Java 8 中引入的 Stream 流为我们提供了一种更加简洁和灵活的方式来处理数据。上述情况都是流对集合进行操作的&#xff0c;但是对于流的创建操作还是不太了解&#xff0…

解密并发编程的时间之谜:揭开Happens-Before的神秘面纱

优质博文&#xff1a;IT-BLOG-CN 一、简介 为什么需要happens-before原则&#xff1a; 主要是因为Java内存模型 &#xff0c; 为了提高CPU效率&#xff0c;通过工作内存Cache代替了主内存。修改这个临界资源会更新work memory但并不一定立刻刷到主存中。通常JMM会将编写的代码…

docker保存镜像出错

报错&#xff1a;open .docker_temp_801673807: Access is denied. 查询后发现是因为C盘权限问题导致失败&#xff0c;修改保存路径&#xff1a;docker save -o D:\nginx.tar nginx:latest后成功。

解决docker使用pandarallel报错OSError: [Errno 28] No space left on device

参考&#xff1a;https://github.com/nalepae/pandarallel/issues/127 在使用pandarallel报错OSError: [Errno 28] No space left on device&#xff0c;根据上述issue发现确实默认使用的MEMORY_FS_ROOT为 /dev/shm&#xff0c;而在docker环境下这个目录大小只有64M&#xff0…

​嵌入式VS纯软件

嵌入式VS纯软件 嵌入式系统开发与纯软件开发之间存在关键差异&#xff0c;涵盖了硬件依赖性、资源限制、实时性要求、安全性和维护等多个方面。最近很多小伙伴找我&#xff0c;说想要一些嵌入式资料&#xff0c;然后我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#x…

序列中排列存在类dp问题+结合组合数学和拆贡献:1014T4

http://47.92.197.167:5283/contest/412/problem/4 赛时就想到枚举开头来拆贡献。 先说一下&#xff0c;对于A我们不关心具体的值&#xff0c;我们只关心哪些位置相等&#xff0c;哪些位置不等&#xff0c;最后乘上一个系数就行 然后对于序列是否存在排列类问题有个常见的dp套…

【API篇】二、源算子API

文章目录 0、demo数据1、源算子Source2、从集合中读取数据3、从文件中读取4、从Socket读取5、从Kafka读取6、从数据生成器读取数据7、Flink支持的数据类型8、Flink的类型提示&#xff08;Type Hints&#xff09; 0、demo数据 准备一个实体类WaterSensor&#xff1a; Data All…

导航栏参考代码

导航栏参考代码 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>导航栏参考代码</title> </head> <body> <table width"858" border"0" align"center"><tr&g…

ASO优化之应用评分评论对APP下载量增长的重要性

APP应用评分和评论对于下载转化率的效果是显著的&#xff0c;是ASO优化重要的因素之一。应用评分是对应用性能的衡量预判&#xff0c;而应用评论是用户向应用提供的反馈总结。 1、评分的重要性。 应用评分显示在应用商店的搜索结果&#xff0c;特色页面&#xff0c;热门图表中…

第五十七章 学习常用技能 - 查看Globals

文章目录 第五十七章 学习常用技能 - 查看Globals查看Globals测试查询并查看查询计划 第五十七章 学习常用技能 - 查看Globals 查看Globals 要查看一般Globals&#xff0c;可以使用 ObjectScript ZWRITE 命令或管理门户中的全局页面。如果正在寻找存储类数据的Globals&#x…

docker搭建nginx+php-fpm

docker run --name nginx -p 8898:80 -d nginx:1.20.2-alpine# 将容器nginx.conf文件复制到宿主机 docker cp nginx:/etc/nginx/nginx.conf /usr/local/nginx/conf/nginx.conf# 将容器conf.d文件夹下内容复制到宿主机 docker cp nginx:/etc/nginx/conf.d /usr/local/nginx/conf…

Linemod算法研究

转载&#xff0c;这篇博客写的比较详细&#xff0c;分析也到位. https://www.cnblogs.com/aoru45/p/16810996.html

Linux查看端口号及进程信息

Linux查看端口号及进程 Linux查看端口号 netstat netstat -tuln显示当前正在监听的端口号以及相关的进程信息 ss ss -tuln与netstat类似&#xff0c;ss也可以用于显示当前监听的端口以及相关信息 isof isof -i :端口号端口号替换为具体要查找的端口号&#xff0c;显示该端…

Mysql数据库 4.图形化界面工具DataGrip

DataGrip工具 选择进入官网安装 ataGrip: The Cross-Platform IDE for Databases && SQL by JetBrains 下载最新版本的可以直接点击 Download 下载&#xff0c;下载其他版本的点击 Other versions 下载其他版本 .选择对应的版本进行下载即可 下载完&#xff0c;得到…

Redis中的BigKey如何发现和处理

文章目录 什么是BigKey?大键的存在通常被认为是不好的,主要原因:常见的bigkey原因: BigKey危害&#xff1f;占用大量内存空间阻塞服务器进程加长持久化时间延长复制时间增加内存碎片加重AOF重写压力降低查找效率 如何发现BigKey&#xff1f;info命令scan命令Redis-cli第三方工…

002数据安全传输-多端协议传输平台:配置Oracle数据库-19c及导入数据信息

002多端协议传输平台&#xff1a;配置Oracle数据库-19c及导入数据信息 文章目录 002多端协议传输平台&#xff1a;配置Oracle数据库-19c及导入数据信息1. 数据库准备2. 导入sql脚本2.1 原版Oracle-11g脚本2.2 新版Oracle-19c脚本2.3 命令行导入脚本 3. 删除系统中数据库信息sql…

在雷电模拟器9上安装magisk并安装LSPosed模块以及其Manager管理器(二)之LSPosed的使用

上一篇已经安装好LSPosed模块及其Manager管理器&#xff0c;参考文章 在雷电模拟器9上安装magisk并安装LSPosed模块以及其Manager管理器&#xff08;一&#xff09;-CSDN博客 安装完成后&#xff0c;在模拟器上出现图标如下&#xff1a; 一、运行LSPosed 二、仓库模块 内容非…