0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

彻底理解编辑距离问题edit distance

算法与数据结构 ? 来源:码农的荒岛求生 ? 2023-04-10 14:04 ? 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

给定两个字符串word1以及word2,返回将word1转为word2需要的最少步骤,在每一步中你可以针对字符串word1进行以下操作:

新增一个字符

删除一个字符

替换一个字符

假如word1是"horse",word2是“ros”,那么你的程序需要返回3,也就是说将word1转为word2至少需要三个步骤:

将word1中的第一个字符h替换为字符r:horse -> rorse,此时word1变为rorse,word1与word2前两个字符相等

将word1中的第三个字符r删掉:rorse -> rose,此时word1变为rose,word1与word2的前三个字符相等

将word1中的最后一个字符删掉:rose -> ros,此时word1与word2相等。

想一想该怎样用动态规划解决这个问题。

选择与子问题

和之前的题目一样,你首先应该找出子问题是什么,子问题与原始问题的依赖关系是什么。

找出子问题的关键在于每一步的选择。

如果word1与word2的第一个字符相等,假设word1是hor、word2是hr,那么我们可以放心的排除掉两个字符串的第一个字符,即EditDistance("hor", "hr")一定等于EditDistance("or", "r"):

4c0324f2-d75a-11ed-bfe3-dac502259ad0.png

此时我们得到了一个子问题EditDistance("or", "r"),原始问题EditDistance("hor", "hr")的值等于该子问题。

真正有趣的是如果word1与word2的第一个字符不相等的情况,假设word1为“hor”,而word2为“ro”,此时根据该问题的规则针对word1的第一个字符有三种操作:

1,在word1的第一个字符前新增(Insert)一个字符r,此时word1变为rhor,由于此时word1 的第一个字符等于word2的第一个字符,可以放心的忽略掉,因此我们得到了子问题EditDistance("hor","o"),由于执行了一次新增操作,因此:

EditDistance("hor","ro")=EditDistance("hor","o")+1

2,将word1的第一个字符删掉(Delete),此时word1变为“or”,我们得到了一个新的子问题EditDistance("or","ro"),由于执行了一次删除操作,因此:

EditDistance("hor","ro")=EditDistance("or","ro")+1

3,将word1的第一个字符替换(Replace )为r,此时word1变为了“ror”,由于word1的第一个字符等于word2的第一个字符,因此可以放心的忽略掉,我们得到了一个新的子问题EditDistance("or","o"),由于执行了一次删除操作,因此:

EditDistance("hor","ro")=EditDistance("or","o")+1

根据题目要求,我们需要得到最小的编辑距离,因此:

EditDistance("hor","ro")=min(EditDistance("hor","o"),
EditDistance("or","ro"),
EditDistance("or","o"))+1

即:

4c20a8ce-d75a-11ed-bfe3-dac502259ad0.png

可以看到,如果word1与word2的第一个字符如果不相等的话那么我们会得到三个子问题,取这三个子问题的最小值然后加1就是原始问题的解。

现在我们找到了子问题与原始问题之间的依赖关系。

实际上,根据上述讨论我们还可以进一步扩展从而得到完整的状态空间树。

4c3f8104-d75a-11ed-bfe3-dac502259ad0.png

从这棵树中可以看到最小的编辑距离是2。

现在你应该清楚的知道该怎样我们是怎样一步步将问题不断的分解为更小的子问题,然后利用子问题的解来得到原始问题的解了。

自顶向下递归代码

上图中每个方框都是一个子问题,决定一个子问题的因素在于word1与word2当前处理到了哪个位置,假设对word1处理到了第i个位置,对word2处理到了第j个位置,因此我们可以对问题进行定义:

intEditDistance(inti,intj);

该函数表示从i到word1的末尾形成的字符串与从j从word2的末尾形成的字符串的编辑距离。

因此如果调用该函数时我们应该这样使用:

EditDistance(0,0);

有了该定义与上述分析,你可以轻而易举的写出这样的递归代码:

stringword1;
stringword2;

intEditDistance(inti,intj){
if(i==word1.length()&&j==word2.length())return0;
if(i==word1.length())returnword2.length()-j;
if(j==word2.length())returnword1.length()-i;

if(word1[i]==word2[j])returnEditDistance(i+1,j+1);
else{
returnmin(EditDistance(i+1,j+1),min(
EditDistance(i,j+1),
EditDistance(i+1,j)))+1;
}
}

我们将word1与word2声明为全局变量,这样你可以清楚的看到决定EditDistance函数值的因素只有这两个参数i和j,i的取值为[0, word1.length()],j的取值为[0, word2.length()],也就是说子问题的个数只有(word1.length() + 1) * (word2.length() + 1) 个,上述递归代码存在大量重复计算问题,因此可以通过增加cache进行优化,这个改动就留给大家啦。

接下来我们着手将自顶向下的递归代码改为自底向上的动态规划代码。

自底向上动态规划代码

由于子问题的个数只有(word1.length() + 1) * (word2.length() + 1) 个,因此可以定义一个相同大小的二维数组dp:

vector>dp(word1.length()+1,vector(word2.length()+1,0));

接下来我们要求解最小子问题,最小子问题就是上述递归代码的递归出口:

if(i==word1.length()&&j==word2.length())return0;

该最小子问题的解包含在了dp数组的初始化中。

接下来的子问题是另外两个递归出口:

if(i==word1.length())returnword2.length()-j;
if(j==word2.length())returnword1.length()-i;

我们可以简单的构造出两种情况下的所有i和j来初始化数组dp,即:

for(intj=word2.length()-1;j>=0;j--)
dp[word1.length()][j]=word2.length()-j;
for(inti=word1.length()-1;i>=0;i--)
dp[i][word2.length()]=word1.length()-i;

最后我们利用两个for循环来构造出所有的i和j,从而将递归函数的最后一部分:

if(word1[i]==word2[j])returnEditDistance(i+1,j+1);
else{
returnmin(EditDistance(i+1,j+1),min(
EditDistance(i,j+1),
EditDistance(i+1,j)))+1;
}

放置在for循环中,并将对递归函数的调用替换为对数组dp的读写:

for(inti=word1.length()-1;i>=0;i--){
for(intj=word2.length()-1;j>=0;j--){
if(word1[i]==word2[j])
dp[i][j]=dp[i+1][j+1];
else
dp[i][j]=min(dp[i+1][j+1],min(dp[i][j+1],dp[i+1][j]))+1;
}
}

最终,完整的动态规划代码为:

intminDistance(stringword1,stringword2){
vector>dp(word1.length()+1,vector(word2.length()+1,0));
for(intj=word2.length()-1;j>=0;j--)
dp[word1.length()][j]=word2.length()-j;
for(inti=word1.length()-1;i>=0;i--)
dp[i][word2.length()]=word1.length()-i;
for(inti=word1.length()-1;i>=0;i--){
for(intj=word2.length()-1;j>=0;j--){
if(word1[i]==word2[j])
dp[i][j]=dp[i+1][j+1];
else
dp[i][j]=min(dp[i+1][j+1],min(dp[i][j+1],dp[i+1][j]))+1;
}
}

returndp[0][0];
}





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 状态机
    +关注

    关注

    2

    文章

    494

    浏览量

    28369

原文标题:彻底理解动态规划:编辑距离

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    距离传输新突破:山泽HDMI线确保信号稳定无损

    、色彩失真甚至无法正常显示的问题。然而,随着技术的进步,山泽HDMI线带来了全新的解决方案,即使在长距离传输下也能确保信号稳定无损,彻底解决了这一难题。 一、高带宽支持,满足超高清需求 要实现长距离下的高清稳定传输,首
    的头像 发表于 08-10 15:06 ?741次阅读

    结构可视化:利用数据编辑器剖析数据内在架构?

    结构可视化聚焦于展示数据的内部结构和各部分之间的关系,使企业能够深入理解数据的组织方式和层次体系,从而更好地进行数据管理和分析。通过结构可视化,企业可以清晰地看到数据的层次结构、关联关系以及数据流
    的头像 发表于 05-07 18:42 ?250次阅读

    Vim编辑器的基本操作

    在代码的世界里,效率是永恒的追求。无论是新手开发者还是资深工程师,都渴望拥有一款能让自己如虎添翼的编辑器。而在Linux生态中,有一款被无数程序员奉为神器、被誉为“效率之王”的编辑器——Vim。它以
    的头像 发表于 05-06 13:41 ?601次阅读
    Vim<b class='flag-5'>编辑</b>器的基本操作

    Linux下Vim编辑器的使用技巧

    【Vim】常用总结? 简介? image 什么是vim?? Linux下两大编辑神器之一 vim ? Linux/Unix下使用最多的编辑器 ? vi的改进版 ? 可能是最难上手的编辑器之一
    的头像 发表于 04-01 17:36 ?695次阅读
    Linux下Vim<b class='flag-5'>编辑</b>器的使用技巧

    DLP EVM GUIA Edit Firmware中怎么打开占空比设置选项?

    DLP EVM GUIA Edit Firmware 中怎么打开占空比设置选项?
    发表于 02-20 06:45

    DLP3010EVM-LC的对焦范围是多少?

    我的使用需求大概是在 Working distance: 300mm~500mm 想请问DLP3010EVM-LC的对焦范围是多少?依照我需求的工作距离能清晰成像吗?
    发表于 02-19 06:41

    DLPC3479 GUI上面的Edit Firmware制作中有几个疑问求解

    GUI上面的Edit Firmware制作中有几个疑问: 1.step2中的RGB 占空比如何修改,目前GUI中这几个参数无法修改。 2.step3中的图片大小和格式是有哪些限制的,目前同样大小
    发表于 02-19 06:23

    什么是爬电距离与电气间隙?

    义爬电距离,可形象理解为一蚂蚁沿绝缘材料表面从一导电部件爬至另一导电部件所经最短路径。它涉及两个导电部件间沿绝缘材料表面测量的最短空间距离,这一距离的设定需综合考量电气设备的额定电压、
    的头像 发表于 01-16 23:05 ?1274次阅读
    什么是爬电<b class='flag-5'>距离</b>与电气间隙?

    请问大家有没有软件L-edit相关的使用教程

    请问大家有没有软件L-edit相关的使用教程
    发表于 12-30 11:04

    字节发布SeedEdit图像编辑模型

    近日,字节跳动公司在其豆包大模型团队的官方网站上,正式公布了其最新的通用图像编辑模型——SeedEdit。这款创新性的图像编辑模型,为用户提供了前所未有的便捷图像编辑体验。 据官方介绍
    的头像 发表于 11-12 10:43 ?827次阅读

    使用OPA1622运放后,为什么无法彻底关断?

    我们在使用OPA1622运放过程中,使用芯片的EN脚拉低关闭运放后,运放输出应该disable,但是现在运放输出声音只是变小,无法彻底关断。更换过OPA1622都是一样的现象,经测量确定EN脚已经拉至GND。
    发表于 10-21 06:16

    Vivado编辑器乱码问题

    我们在日常开发中经常使用sublime、vim、vs code等第三方的编辑器,这些编辑器可以使用很多插件来提高我们的编码效率,但是也往往会带来乱码的问题。我一般使用的是sublime来进行编码
    的头像 发表于 10-15 17:24 ?2772次阅读
    Vivado<b class='flag-5'>编辑</b>器乱码问题

    OrCAD Capture 17.2 点击edit simulation profile没有反应

    请问OrCAD capture 17.2点击edit simulation profile后没有反应应该怎么解决呢,可以正常绘制原理图,也可以跑仿真,但是没有simulation setting窗口弹出,无法修改仿真设置,求助各位!
    发表于 09-19 22:28

    vim编辑器命令模式使用方法

    Vim编辑器是一款功能强大的文本编辑器,广泛应用于程序员和开发者的日常工作中。Vim编辑器拥有多种模式,其中命令模式(Command mode)是最基本的模式之一,它允许用户执行各种命令来操作
    的头像 发表于 08-30 15:01 ?1155次阅读

    vim编辑器如何使用

    Vim编辑器是一个功能强大的文本编辑器,它基于Vi进行改进,并增加了许多新特性。Vim编辑器的使用主要涉及其不同的工作模式及相应操作。以下是Vim编辑器的基本使用方法: 一、Vim
    的头像 发表于 08-30 14:58 ?970次阅读