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

莱文斯坦距离

在搜索引擎中会有搜索词纠错的功能。这个功能背后的原理是编辑距离。

编辑距离

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

动态规划——莱文斯坦距离相关推荐

  1. 编辑距离——莱文斯坦距离(Levenshtein distance)

    在信息论和计算机科学中,莱文斯坦距离是一种两个字符串序列的距离度量.形式化地说,两个单词的莱文斯坦距离是一个单词变成另一个单词要求的最少单个字符编辑数量(如:删除.插入和替换).莱文斯坦距离也被称做编 ...

  2. 莱文斯坦距离(编辑距离)算法 (Levenshtein Distance Algorithm)

    什么是 莱文斯坦距离算法 (Levenshtein Distance Algorithm) ? Levenshtein Distance,莱文斯坦距离,通常被称为编辑距离(Edit Distance) ...

  3. LD(Levenshtein distance)莱文斯坦距离----编辑距离

    链接:https://ac.nowcoder.com/acm/contest/327/G 来源:牛客网 G处女座与复读机 题目描述 一天,处女座在牛客算法群里发了一句"我好强啊", ...

  4. 相似度算法--莱文斯坦距离加入同义词逻辑

    一. 背景 在问题检索中,依赖文本相似度给用户做推荐问题,假设1.0分为满分,那么: 1.0分表示完全匹配:可以将问题准确推送给用户 0.8分表示高度相似:可以将问题推荐给用户 0.6分表示低度相似: ...

  5. 编辑距离算法【莱文斯坦距离、Levenshtein 算法】

    文章目录 算法概述: 应用 与其他编辑距离度量的关系 问题定义: 解析: 例题: 参考链接: 算法概述: 在信息论和计算机科学中,莱文斯坦距离是一种两个字符串序列的距离度量.形式化地说,两个单词的莱文 ...

  6. 动态规划 | 可以用在核酸检测的算法:莱文斯坦算法

    核酸检测经此一疫已经成为了人尽皆知的检测手段,但是你可曾想过经过仪器检测出来的 DNA序列是如何与正常情况下的 DNA 序列做对比的呢?面对整村的核酸检测结果,计算工作肯定需要电脑来完成,如果我们想知 ...

  7. 【数据结构与算法】【算法思想】动态规划

    贪心算法 回溯算法 分治算法 动态规划 贪心:一条路走到黑,就一次机会,只能哪边看着顺眼走哪边 回溯:一条路走到黑,无数次重来的机会,还怕我走不出来 (Snapshot View) 动态规划:拥有上帝 ...

  8. 动态规划应用--搜索引擎拼写纠错

    文章目录 1. 字符串相似度 1.1 莱文斯坦距离 1.2 最长公共子串长度 2. 计算编辑距离 2.1 莱文斯坦距离 2.2 最长公共子串长度 3. 搜索引擎拼写纠错 4. 练习题 在 Trie树那 ...

  9. leetcode动态规划(python与c++)

    1 . 斐波那契数 class Solution:def fib(self, n: int) -> int:# if n==0:# return 0# elif n==1:# return 1# ...

最新文章

  1. 17,Scatter函数
  2. 编者序:初衷、计划、要求、优势、目标和展望
  3. 业界丨一文看懂AI人才百万美元年薪因何而来?
  4. MySQL-体系结构以及常用存储引擎MyISAM和InnoDB初探
  5. 通俗解释随机森林算法
  6. select查询语句执行顺序
  7. 【牛客 - 301哈尔滨理工大学软件与微电子学院第八届程序设计竞赛同步赛(高年级)】小乐乐搭积木(状压dp)
  8. 【Android UI设计与开发】9:滑动菜单栏(一)开源项目SlidingMenu的使用和示例-转...
  9. html制作翻牌游戏,基于javascript实现句子翻牌网页版小游戏
  10. 使用MyBatis快速生成代码
  11. 涂鸦蓝牙SDK开发系列教程——4.烧录授权
  12. 德芙网络营销策略ppt_德芙网络营销案例ppt采集
  13. Classic Shell不起作用(失效)的解决
  14. 集合中篇—栈与队列区块链
  15. 鸿蒙开发实例 | 分布式涂鸦
  16. 对一个windows2000注册表项的粗略分析(转)
  17. [NIPS-18] Generalizing to Unseen Domains via Adversarial Data Augmentation
  18. python--20行代码爬取【全职高手】小说
  19. linux查看网口流量ifconfig,ifconfig与网络流量监控
  20. win7鼠标设置在哪_WIN7系统下ODIS和5054蓝牙配对教程

热门文章

  1. 把一张合成图分拆出各个小图
  2. ZOJ 2562 More Divisors
  3. mysql事务模式怎么查_Mysql InnoDB中的查询事务模式与锁定select ..for update
  4. 15-Flutter移动电商实战-商品推荐区域制作
  5. Android 下载进度条, 自定义加载进度条,loading动画
  6. mpvue 从零开始 女友的来电 4 flyio
  7. jquery mobile 从一个html的page跳转到另一个html的page
  8. javascript event
  9. 天猫整站SSM-后台分类管理-增加(做个人学习笔记整理用)
  10. 如何理解HTTP协议的 “无连接,无状态” 特点?