动态规划——莱文斯坦距离

文章出处:极客时间《数据结构和算法之美》-作者:王争。该系列文章是本人的学习笔记。

莱文斯坦距离

在搜索引擎中会有搜索词纠错的功能。这个功能背后的原理是编辑距离。
在这里插入图片描述

编辑距离

编辑距离是量化两个词之间的相似度。 编辑距离是指将一个字符串变为另外一个字符串,需要的最少编辑操作次数。编辑操作包含新增一个字符、修改一个字符、删除一个字符。编辑次数越少,编辑距离越小,两个字符串相似度越大。如果两个字符串完全相同,编辑距离为0。

根据所包含的编辑操作种类的不同,编辑距离有多种不同的计算方式,比较著名的有莱文斯坦距离(Levenshtein distance)和最长公共子串长度(Longest common substring length)。其中,莱文斯坦距离允许增加、删除、替换字符这三个编辑操作,最长公共子串长度只允许增加、删除字符这两个编辑操作。

莱文斯坦距离

用莱文斯坦距离替换两个字符串的过程。

回溯解法

莱文斯坦距离将一个字符串替换为另外一个字符串,计算最少的编辑次数。需要考虑字符串中每一位上的字符。如果字符相同怎么处理,字符不同怎么处理。这是一个多阶段决策最优解模型

贪心、回溯、动态规划都可以解决这类问题。我们先用回溯法解决,看是不是有重复子问题。如果没有,回溯就是最优解;如果有重复子问题,那就需要用动态规划优化。

回溯是一个递归处理问题的过程。假设我们要把a字符串替换为b字符串。如果a[i]和b[j]匹配,则i++,j++。如果不匹配,可以采取的措施有:
1 删除a[i],然后递归考察a[i+1]和b[j];
2 在a[i]前面插入字符b[j],然后递归考察a[i]和b[j+1];
3 将a[i]替换为b[j],然后递归考察a[i+1]和b[j+1]。

翻译成代码:

	public class LevenshteinDistance {private char[] a = "mitcmu".toCharArray();private char[] b = "mtacnu".toCharArray();private int n = a.length;private int m = b.length;private int minEdist = Integer.MAX_VALUE;private void lwstBT(int i,int j,int edist){if(i==n || j==m){if(j<m) {edist += m-j;}if(i<n){edist += n-i;}minEdist = Math.min(edist,minEdist);return;}if(a[i]==b[j]){lwstBT(i+1,j+1,edist);}else{lwstBT(i+1,j,edist+1);//删除a[i]lwstBT(i,j+1,edist+1);//在a[i]前面插入b[j]lwstBT(i+1,j+1,edist+1);//修改a[i]=b[j]}}public void lwstBT(){lwstBT(0,0,0);}public static void main(String[] args){LevenshteinDistance l =  new LevenshteinDistance();l.lwstBT();System.out.println(l.minEdist);}
}

我们依据回溯代码来看下递归树。

递归树的每一个节点表示一种状态,用(i,j,edist)表示,i表示指针在a字符串的位置,j表示指针在b字符串的位置,(i,j)都表示将要处理的字符位置,edist表示到达(i,j)时已经执行的编辑次数。递归树中的一条边对应一种处理方式。

从树中能够看出(i,j)相同的节点很多。例如(2,2)、(2,3)。同一个状态的节点只要保留一个编辑距离最小的就可以。因为我们的目标就是找编辑距离最小的。这样也可以避免递归树节点指数级增长。

我们接着看下状态转移方式。状态(i,j)可能从(i-1,j-1)、(i-1,j)、(i,j-1)这三个状态的任一状态转变过来。

状态表法

接下来我们按照这种方式,计算状态转移表。我们画出一个二维状态表,表中的行、列表示字符串在a、b中的位置,表中的数值表示从(0,0)到这个位置需要执行的最短编辑次数。需要说明的是这个编辑次数是包含本次操作的。与递归树的状态中数值的含义略微不同。

这里说一下填表的过程。
(0,0):m->m 不需要编辑。
(0,1)m->mt 需要 一次编辑。

这张表比较难填写,我没明白怎么填写的。如果从(0,0)开始算后面还能算明白,想往回递推就搞不懂了。
比较简单的理解就是想决定(2,2)的值,从(1,1). (1,2). (2,1)三个值中选择一个最小值,再加1。就对了。加1是因为a!=t。(图下面有补充2021-1-1)

这里补充一下之前不能理解的地方。例如想要到达dp[2][2]这个状态,就是说想要字符串"mit"变为"mta"。
我们已经知道dp[1][1]=1,也就是说从"mi"最少有1次操作,可以变为"mt"。这个时候,我们在"mi"变成的"mt"后面添加一个t,在"mt"字符串后面添加一个a,我们将t替换为a(mtc->mta),就可以实现将字符串"mit"变为"mta"。也就是说dp[1][1]+1。这里需要说明的是,如果同时追加的都是a字符的话,那就不用编辑操作。(mta->mta)不需要操作,那这时候的编辑次数就是dp[i-1][j-1]。
我们已经知道dp[1][2]=2,也就是说从"mi"变为"mta"需要2次操作。这时候在mi后面追加字符t,那么只需要把字符t删除(mtat->mta),就能实现从"mit"变为"mta"。也就是说dp[1][2]+1。
我们已经知道dp[2][1]=1,也就是说从"mit"变为"mt"需要1次操作。那么只需要在后面添加一个a字符(mt->mta),就能实现从"mit"变为"mta"。也就是说dp[2][1]+1。

状态转移方程

根据状态转移方式很容易得到状态转移方程。
如果a[i]=b[j]

 min_edist(i.j) = min(min_edist(i-1,j)+1,min_edist(i,j-1)+1,min_edist(i-1,j-1));

如果a[i]!=b[j]

 min_edist(i.j) = min( min_edist(i-1,j)+1,min_edist(i,j-1)+1,min_edist(i-1,j-1)+1 );

DP代码:

	public int lwstDP(char[] a, int n, char[] b, int m) {int[][] minDist = new int[n][m];for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(i==0 && j==0){minDist[0][0] = a[0]==b[0]?0:1;}else{minDist[i][j] = Integer.MAX_VALUE;if(i>0 && j>0){minDist[i][j] = Math.min(minDist[i][j],minDist[i-1][j-1]+(a[i]==b[j]?0:1));}if(i==0 && j>0){minDist[i][j] = Math.min(minDist[i][j],minDist[i][j-1]+1);}if(i>0 && j==0){minDist[i][j] = Math.min(minDist[i][j],minDist[i-1][j]+1);}}}}return minDist[n-1][m-1];}

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

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

相关文章

[密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第18篇]画一个/描述ECB,CBC,CTR模式的操作

操作模式:块密码的安全性依赖于加解密一个固定长度的明文块.当加密或者解密消息的时候,块是被需要的.我们使用一种操作模式将明文的多个块链接在一起.我们会知道,这种链接在一起的方法是十分重要. 电子密码本(ECB)模式:加密,解密. ECB模式是最直接的方法.明文被分割成m块.每一…

动态规划——最长公共子序列长度

最长公共子序列长度是编辑距离的另外一种表示方法。只允许添加、删除字符两种惭怍。它表征的是两字符串之间的相似度。 解决思路是&#xff1a; 如果a[i]b[j]&#xff0c;则 公共子序列长度加1&#xff0c;继续考察a[i1]和b[j1]。 如果a[i]!b[j]&#xff0c;则删除a[i]或者在b[…

【转】store_schema.sql

来源&#xff1a;https://www.cnblogs.com/cenliang/p/5162939.html 这个是《精通Oracle Database 12c SQL & PL/SQL编程(第三版)》的数据&#xff0c;我用的是Oracle11g版本&#xff0c;做了些微调&#xff0c;如日期格式等。 给自己留个存档&#xff0c;以备不时之需~ --…

[密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第20篇]Merkle-Damgaard hash函数如何构造

这里讲的是MD变换,MD变换的全称为Merkle-Damgaard变换.我们平时接触的hash函数都是先构造出一个防碰撞的压缩函数.然后先证明这个小的,固定长度的压缩函数是安全的,然后再用它构造一个任意长度的哈希算法.虽然存在很多其它的构造方法,MD是迄今为止最常用的(至少是被用到最多的)…

第五十六期:IPv6只是增加了地址数量?其实真相并没有那么简单!

究竟什么是IPv6?它到底是干啥用的?IPv6的全称是Internet Protocol version 6。其中&#xff0c;Internet Protocol译为“互联网协议”。所以&#xff0c;IPv6就是互联网协议第6版。 作者&#xff1a;小枣君 10月20日&#xff0c;在乌镇举办的第六届世界互联网大会上&#x…

动态规划——背包问题升级

文章出处&#xff1a;极客时间《数据结构和算法之美》-作者&#xff1a;王争。该系列文章是本人的学习笔记。 背包能够承受的总重量一定w&#xff0c;每个物品的总量不同int[] weight表示&#xff0c;每个物品的价值不同用int[] values表示。怎么放才能让背包中物品不超过最大…

MacOS安装过程需要注意的几个问题

如果是单系统, 会简单很多, 直接按照安装教程来走就行, 一步一步来就很简单. 多系统安装, 则需要注意硬盘分区表方案. MBR 和 GUIP(简称GPT). Mac系统无法原生安装于MBR硬盘上,引导方案应选择UEFI/GPT 方案要设置磁盘分区表为GUIP.(使用DiskGenius等磁盘管理工具, 可以无损, 但…

spring学习(10):创建项目(自动装配)

首先创建项目 pom.xml的配置文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://mav…

[密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第21篇]CRT算法如何提高RSA的性能?

CRT加速RSA&#xff1a;https://www.di-mgt.com.au/crt_rsa.html 转载链接&#xff1a;https://www.cnblogs.com/zhuowangy2k/p/12245513.html

javascript优先级注意点

javascript 优先级 注意一下 && 和 号之间的优先级关系 请首先看如下代码, 判断下 && 和 的优先级 const emptyObj {name: "kylin"}; if (emptyObj && emptyObj.name "kylin") {console.log("my name is kylin"); }…

动态规划——最长递增子序列

题目 我们有一个数字序列包含 n 个不同的数字&#xff0c;如何求出这个序列中的最长递增子序列长度&#xff1f;比如 2, 9, 3, 6, 5, 1, 7 这样一组数字序列&#xff0c;它的最长递增子序列就是 2, 3, 5, 7&#xff0c;所以最长递增子序列的长度是 4。 回溯法 数组长度为n&a…

spring学习(11):使用配置类

CompactDisc类 package soundSystem;import org.springframework.stereotype.Component;Component public class CompactDisc {public CompactDisc() {super();System.out.println("compactdisc无参构造方法");}public void play(){System.out.println("正在播…

[密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第22篇]如何用蒙哥马利算法表示一个数字和多个相乘的数字

这是一系列博客文章中最新的一篇&#xff0c;该文章列举了“每个博士生在做密码学时应该知道的52件事”:一系列问题的汇编是为了让博士生们在第一年结束时知道些什么。 安全和效率 密码学的目标是设计高度安全的密码学协议,但是同时这些协议也应该被有效率的实现.这样就可以一…

动态规划——双11既可以薅羊毛还能花钱最少

淘宝的“双十一”购物节有各种促销活动&#xff0c;比如“满 200 元减 50 元”。假设你的购物车中有 n 个&#xff08;n>100&#xff09;想买的商品&#xff0c;希望从里面选几个&#xff0c;在凑够满减条件的前提下&#xff0c;让选出来的商品价格总和最大程度地接近满减条…

spring学习(12):使用junit4进行单元测试

pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 …

ELK电子书籍

Elasticsearch in Action(英文版).pdfElasticsearch实战 in action(中文版).pdfElasticsearch技术解析与实战.pdfElasticsearch大数据搜索引擎.pdfELK Stack权威指南 第2版.pdf深入理解ElasticSearch.pdfElasticsearch服务器开发&#xff08;第2版&#xff09;.pdf下载地址:链接…

[密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第23篇]写一个实现蒙哥马利算法的C程序

这是一系列博客文章中最新的一篇&#xff0c;该文章列举了“每个博士生在做密码学时应该知道的52件事”:一系列问题的汇编是为了让博士生们在第一年结束时知道些什么。 这次博客我将通过对蒙哥马利算法的一个实际的实现&#xff0c;来补充我们上周蒙哥马利算法的理论方面。这个…

spring学习(13):使用junit4进行单元测试续

加入spring test.jar包 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven…

动态规划几种状态剪裁比较

我们前面看过了0-1背包问题&#xff0c;矩阵最短路径长度问题和莱文斯坦最短 编辑距离问题。 在三个问题的状态树中。 0-1背包问题&#xff1a;用(i,cw)表示状态。i 表示要将要处理第i个物品&#xff0c;cw表示当前总重量。因为(i,cw)有重复的&#xff0c;所以使用动态规划。 …

《java程序员修炼之道》pdf书籍

链接&#xff1a;https://pan.baidu.com/s/1bbsTPCpUNI9klh40-8Be7w 提取码&#xff1a;pc57 转载于:https://www.cnblogs.com/pyweb/p/10995145.html