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

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

莱文斯坦距离

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

编辑距离

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

[密码学基础][每个信息安全博士生应该知道的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…

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

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

题目 我们有一个数字序列包含 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 …

[密码学基础][每个信息安全博士生应该知道的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…

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

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

[密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第24篇]描述一个二进制m组的滑动窗口指数算法

这是一系列博客文章中最新的一篇&#xff0c;该文章列举了“每个博士生在做密码学时应该知道的52件事”:一系列问题的汇编是为了让博士生们在第一年结束时知道些什么。 二进制算法 二进制模幂算法和传统的求幂的二次方方法非常像。实际上&#xff0c;唯一的不同就是我们把N表示…

第五十八期:从0到1 手把手教你建一个区块链

近期的区块链重回热点&#xff0c;如果你想深入了解区块链&#xff0c;那就来看一下本文&#xff0c;手把手教你构建一个自己的区块链。 作者&#xff1a;Captain编译 近期的区块链重回热点&#xff0c;如果你想深入了解区块链&#xff0c;那就来看一下本文&#xff0c;手把手…

动态规划——硬币找零思路

找零的两种问题 硬币找零问题&#xff0c;有两种。一种用贪心解决&#xff0c;一种用动态规划解决。 问题1&#xff1a;假设我们有 v1&#xff0c;v2&#xff0c;……&#xff0c;vn&#xff08;单位是元&#xff09;这些币值的硬币&#xff0c;它们的张数分别是 c1、c2、…, …

[密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第25篇]使用特殊的素数定义GF(p)和GF(2^n)的方法

这是一系列博客文章中最新的一篇&#xff0c;该文章列举了“每个博士生在做密码学时应该知道的52件事”:一系列问题的汇编是为了让博士生们在第一年结束时知道些什么。 当实现密码学方案时&#xff0c;一个最频繁调用的操作就是模运算。不幸的是&#xff0c;尽管模块化的使用非…

第五十九期:如何在Windows 10中执行Windows Defender离线扫描?

如果你使用Windows并经常上网浏览&#xff0c;强烈建议使用防病毒/安全保护软件。Windows 10随带一款名为Windows Defender/Security的内置防病毒保护工具&#xff0c;在过去几年不断成熟&#xff0c;已成为一种优秀的安全解决方案。 作者&#xff1a;布加迪编译 如果你使用Wi…

[密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第26篇]描述NAF标量乘法算法

这是一系列博客文章中最新的一篇&#xff0c;该文章列举了“每个博士生在做密码学时应该知道的52件事”:一系列问题的汇编是为了让博士生们在第一年结束时知道些什么。 [1] Hankerson, Darrel, Scott Vanstone, and Alfred J. Menezes. “Guide to elliptic curve cryptography…

第六十一期: 从7600万个5G连接中,我们发现了7种最有前景的5G物联网应用

近日&#xff0c;物联网市场调研机构IoT Analytics调研了44种不同的5G物联网用例&#xff0c;预计到2025年&#xff0c;这些用例将覆盖7600万个5G节点&#xff0c;本文重点介绍了其中7种最有市场前景的5G物联网用例。 作者&#xff1a;物联网智库 5G是第五代移动通信技术&…