【学习笔记】字符串编辑距离(字符串相似度)

字符串编辑距离是一个很经典的问题了,以前参加编程比赛、笔试面试的时候经常会碰到,今天看《编程之美》的3.3节中又遇到了该问题,又重新回顾了一边,顺便在这里总结一下。


【问题描述】

给定一个源字符串和目标字符串,能够对源串进行如下操作:
   1.在给定位置上插入一个字符
   2.替换任意字符
   3.删除任意字符

求通过以上操作使得源字符串和目标字符串一致的最小操作步数。(问题的详细描述参考《数学之美》3.3)。


【解题思想】

简单描述一下解该题的思想,源字符串和目标字符串分别为str_a、str_b,二者的长度分别为la、lb,定义f[i,j]为子串str_a[0...i]和str_b[0...j]的最小编辑距离,简单分析可知求得的str_a[0...i]和str_b[0...j]的最小编辑距离有一下三种可能:
(1)去掉str_a[0...i]的最后一个字符跟str_b[0...j]匹配,则f[i, j]的值等于f[i-1, j]+1;
(2)去掉str_b[0...j]的最后一个字符跟str_a[0...i]匹配,则f[i, j]的值等于f[i, j-1]+1;
(3)去掉str_a[0...i]和str_b[0...j]的最后一个字符,让二者匹配求得f[i-1, j-1],计算f[i, j]时要考虑当前字符是否相等,如果str_a[i]==str_b[j]说明该字符不用编辑,所以f[i, j]的值等于f[i-1, j-1],如果str_a[i]!=str_b[j]说明该字符需要编辑一次(任意修改str_a[i]或者str_b[j]即可),所以f[i, j]的值等于f[i-1, j-1]+1。
因为题目要求的是最小的编辑距离,所以去上面上中情况中的最小值即可,因此可以得到递推公式:

f[i, j] = Min ( f[i-1, j]+1,   f[i, j-1]+1,   f[i-1, j-1]+(str_a[i]==str_b[j] ? 0 : 1) )

下图是维基百科【参考文献1】中的递推式,实际上与上面的是一样的:



【解题方法】

有了上面的递推公式便可以编程实现了,一般会有两种解法。

第一种:递归方法(也是动态规划方法),通过上面的递推公式写出递归程序,这里就不贴出源代码了,网上到处都是。用递归方法要注意一个问题,从f[la-1, lb-1]往下递归时会有重复计算f[x, y](x<la-1, y<lb-1)的情况,是非常影响程序效率的,所以一般会建立f值得缓存,因此递归方法的程序会有两个版本,一个是最简单最原始的版本——不带f值缓存,另一个版本带f值缓存,维基百科【参考文献1】中给出了这两个版本的程序。

第二种:递推方法(也可称为矩阵标记法),通过分析可知可以将f[i, j]的计算在一个二维矩阵中进行,上面的递推式实际上可以看做是矩阵单元的计算递推式,只要把矩阵填满了,f[la-1, lb-1]的值就是要求得最小编辑距离,具体的过程这里不再赘述,可以参考【参考文献2】,程序网上也有很多。

【文献3】提供了上面提到的三种版本的程序。【文献4】是最全面的文章,归纳总结了相似的问题。


【补充】

最后补充一下“字符串相似度”的定义,《编程之美》3.3节中提到:相似度表示为1/(最小编辑距离 + 1),比如abcdefg和abcdef最小编辑距离为1(通过abcdefg删除g一个操作),这样相似度为1/2 = 0.5。


【参考文献】

http://en.wikipedia.org/wiki/Levenshtein_distance
http://www.cnitblog.com/ictfly/archive/2005/12/27/5828.aspx
http://blog.163.com/kevinlee_2010/blog/static/16982082020111123111835146/
http://www.cnblogs.com/zhangchaoyang/articles/2012070.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值