最小编辑距离问题
来源:互联网 发布:linux压缩和解压命令 编辑:程序博客网 时间:2024/05/21 14:07
最近在看一些react里面的Virtual DOM的相关文章,看到了看到了 livoras 的这篇文章,其中讲到了在比较两棵虚拟DOM树的差异的时候会用到字符串最小编辑距离的算法,因为那篇文章主要讲述的点并不在此,所以对于这个算法着墨不多,于是就认真去研究了下这个算法,在此处做个记录吧。
问题描述:
给定两个字符串m和n,只允许进行如下三种操作:
- 插入,例如:ab -> abc
- 删除,例如:abc -> ab
- 替换,例如:abc -> abd
那么请求出将m变成n的最小操作次数(也就是最小编辑距离)
求解这个问题,一般有两种思路:递归和动态规划。
递归:
首先假设字符串m有j位,字符串n有k位,此时我们将m -> n的最小编辑距离记为d[j][k],此时我们能总结出如下的规律:
- 当m[j] === n[k](m[j]和n[k]为字符串的最后一位)时,例如:asd -> jkl时候,很明显此时最后一位是不需要做变动的,因此最小编辑距离等同于:as -> jk,那么我们可以确定d[j][k] === d[j - 1][k - 1];
- 当 m[j] !== n[k] 时,字符串asd到字符串jkl的d[j][k] 又可以分为如下三种情况:
- asd -> jkl的最小编辑距离 = as -> jkl的最小编辑距离+1(加一个插入d的操作),对此可以描述为:d[j][k] === d[j - 1][k] + 1;
- asd -> jkl的最小编辑距离 = asdl -> jkl的最小编辑距离 + 1(加一个删除l的操作),此时可以看到asdl与jkl的最后一位相等,因此可以再次简化为:asd -> jk的最小编辑距离 + 1,对此可以描述为:d[j][k] = d[j][k - 1] + 1;
- asd -> jkl的最小编辑距离 = asl -> jkl的最下编辑距离 + 1(加上一个将d替换成l的操作),此时asl与jkl的最后一位再次相等,因此同样可以进行简化为:as -> jk的最下编辑距离 + 1,对此可以描述为:d[j][k] = d[j - 1][k - 1] + 1;
- 如果m的长度为 0,那么 m -> n 的最小编辑距离为n的长度;反过来,如果n的长度为 0,那么 m -> n 的最小编辑距离为m的长度(全部执行删除操作),可以描述为:d[j][0] = j,d[0][k] = k;
那么我们可以开始按照上述思路写代码了:
/** * 递归算法 * @param {string} m * @param {string} n * @param {number} j 字符串m的长度 * @param {number} k 字符串n的长度 * @returns {number} 从 m -> n 的最小编辑距离 */function editDistance(m, n, j, k) { // 触碰到边界条件 if (k === 0) { return j; } else if (j === 0) { return k; } else if (m[j - 1] === n[k - 1]) { // 当最后一位相等的时候 return editDistance(m, n, j - 1, k - 1); } else { // 当最后一位不相等的时候,取最小值 const d1 = editDistance(m, n, j - 1, k) + 1; const d2 = editDistance(m, n, j, k - 1) + 1; const d3 = editDistance(m, n, j - 1, k - 1) + 1; return Math.min(d1, d2, d3); }}
这个代码虽然能实现,但是有个严重的问题,就是代码的性能很低下,时间复杂度是指数增长的,因此可以考虑另外一种实现。
动态规划
动态规划看起来跟递归很像,不过推理逻辑正好是反过来的。递归的逻辑是:“要求得 d[j][k],先要求得 d[j-1][k-1]……”,动态规划的逻辑是:“先求得 d[j-1][k-1],再求 d[j][k]……”这是它们的主要区别。
同样先举个例子,有两个字符串分别是m = ‘asdfgh’ 和 n = ‘zscv’,我们一步步的来进行如下处理:
1:首先将我们的两个字符串放入如下的矩阵中去:
2: 此时我们可以将d[0][0]、d[0][1] … d[0][4]等等的最小编辑距离填入此矩阵:
3: 这个时候我们可以去计算d[1][1]了,上面在讲述递归的方法的时候我们已经说过计算d[j][k]的时候,当m[j] !== n[k]时会有三种方法,此时 d[0][1] + 1 = 2、 d[1][0] + 1 = 2、d[0][0] + 1 = 1,因此可以得知d[1][1]的最小编辑距离就是1,然后这一行后面的我们都可以直接进行插入操作一次递增即可,由此得出下面的矩阵:
4:这个时候我们开始去计算d[2][2],首先我们可以按照之前的方式计算出d[1][2] = 2,填入矩阵,这个时候再看d[2][2],此时我们可以发现矩阵正好满足条件m[j] === n[k],因此此时d[j][k] === d[j - 1][k - 1] = 1,填入矩阵如下:
5:不断重复上述步骤直到完成矩阵:
此时我们自然可以看到d[j][k] = 5;
按照思路写下代码:
/** * 动态规划算法 * @param {string} m * @param {string} n * @return {number} 从 m → n 的最小编辑距离 */function dynamicPlanning(m, n) { const lenM = m.length; const lenN = n.length; const d = []; for (let i = 0; i <= lenM; i++) { d[i] = []; d[i][0] = i; } for (let j = 0; j <= lenN; j++) { d[0].push(j); } for (let i = 1; i <= lenM; i++) { for (let j = 1; j <= lenN; j++) { if (m[i - 1] === n[j - 1]) { d[i][j] = d[i - 1][j - 1]; } else { const d1 = d[i - 1][j] + 1; const d2 = d[i][j - 1] + 1; const d3 = d[i - 1][j - 1] + 1; d[i][j] = Math.min(d1, d2, d3); } } } return d[lenM][lenN];}
这次的算法复杂度就为线性了
- 最小编辑距离问题
- 最小编辑距离问题(Edition Distance)
- POJ3356:AGTC(最小编辑距离问题)
- 最小编辑距离
- 最小编辑距离
- 最小编辑距离
- 最小编辑距离
- 计算最小编辑距离
- 最小编辑距离
- 最小编辑距离代码
- 最小编辑距离
- 最小编辑距离
- 最小编辑距离-poj3356
- 最小编辑距离算法
- 最小编辑距离
- 牛客网--最小操作数问题--编辑距离及编辑距离算法
- 最小编辑距离的理解
- 最小编辑距离python实现
- 20171031
- Hibernate框架(一)
- 20171031|每日练习
- 对于前后端分离技术的理解和实现
- 文件的读写以及大小写转换
- 最小编辑距离问题
- Mac下多版本JDK安装
- 欢迎使用CSDN-markdown编辑器
- RecyclerView嵌套Spinner onItemSelected问题
- 异常crrently using minified code outside of NODE_ENV === 'production'. This means that 。。。
- PHP设计模式-适配器模式
- 缓存
- Linux python “no module named numpy” 解决方案
- Redission--基于redis的分布式协调客户端