动态规划求编辑距离 - 残阳似血的博客

动态规划求编辑距离 - 残阳似血的博客

动态规划求编辑距离

位于分类 自然语言处理

这两天在写一个简单的单词拼写检查器(Spell checker),本来求编辑距离只是其中的一个子问题,现在把它罗列出来,是因为鉴于看到一些书,把本来不是很难的问题讲得很复杂,而且不知道是不是一些作者,为了显得自己水平之高,几乎没有任何的推导,只有一堆结果罗列。当然这些都是题外话了,但是,书者,传道授业解惑也,若是使读者迷惑不能自拔,不知是读者愚钝,还是书之不书。

现在我们开始切入正题。先来说说什么是编辑距离,编辑距离是一种字符串之间相似程度的计算方法。按照Damerau给出的定义,即两个字符串之间的编辑距离等于使一个字符串变成另外一个字符串而进行的(1)插入、(2)删除、(3)替换或(4)相邻字符交换位置而进行操作的最少次数。用ed来表示编辑距离。

比如:ed("recoginze", "recognize") == 1(需要交换两个相邻字符"i"和"n"的位置)

ed("sailn", "failing") == 3(需要将"s"换成"f",在字母"l"后边插入"i","n"后面插入"g")。

有的时候,变换并不唯一,重要的是要求出这一些变换路径中最短的数量。

关于编辑距离的求法,普遍的采用的是动态规划方法。但是,目前网上的资料中提及的编辑路径并不准确,缺少了第(4)步的处理。详细的同学们可以查阅这两篇文章——【串和序列处理 2】字符串编辑距离算法(Java)和动态规划求编辑距离——算法解题报告(C++)。

下面给出维基上对动态规划的定义:

动态规划是一种在数学和计算机科学中使用的,用于求解包含重叠子问题的最优化问题的方法。其基本思想是,将原问题分解为相似的子问题,在求解的过程中通过子问题的解求出原问题的解。

其实就是把一个复杂的最优解问题分解成一系列较为简单的最优解问题,再将较为简单的最优解问题一步步分解,直到能够一眼看出为止。

我们拿"sailn"和"failing"这两个字符串作例子。首先我们定义这样一个函数——edit(i, j),它表示字符串1的长度为i的子串到字符串2的长度为j的子串的编辑距离。

首先我们作出初始化edit(0, j) = j(字符串1子串长度为0,字符串2子串有多少个字符,就作多少次增加操作;于是同理,edit(i, 0) = i。)

这里,我们要注意到对于操作(4),即交换相邻字符串的操作,我们要把某个字符通过这个操作到另一个位置上,我们最多只能执行一次操作,即只能移动到邻位上。原因是什么呢?这是因为,移动两次的话,就没有优势了,它的操作等于两次替换操作的操作数。大于2次的时候,移动操作会更差。所以,我们要进行操作(4),最多只发生一次操作。

我们可以得出这样一段动态规划公式:

  1. 如果i == 0 且 j == 0,edit(i, j) = 0
  2. 如果i == 0 且 j > 0,edit(i, j) = j
  3. 如果i > 0 且j == 0,edit(i, j) = i(2、3点之前已经陈述)
  4. 如果0 < i ≤ 1  且 0 < j ≤ 1 ,edit(i, j) == min{ edit(i-1, j) + 1, edit(i, j-1) + 1, edit(i-1, j-1) + f(i, j) },这里当字符串1的第i个字符不等于字符串2的第j个字符时,f(i, j) = 1;否则,f(i, j) = 0。
  5. 如果i > 1且 j > 1时,这个时候可能出现操作(4),由之前的推导,我们只能交换一次,否则就没有意义。这个时候在比较最小值中可能加入edit(i-2, j-2) +1,什么时候加入呢?假设i-2长度的字符串1子串和j-2长度的字符串2子串已经得出最优解,这个时候如果s1[i-1] == s2[j] 并且s1[i] == s2[j-1],这时就在比较值中加入edit(i-2, j-2) + 1(这个1是交换一次的操作)

我们来把这个过程演绎一遍。我们首先给出这样一个矩阵:

  0 f a i l i n g
0                
s                
a                
i                
l                
n                

在经过初始化之后,矩阵变成这样:

  0 f a i l i n g
0 0 1 2 3 4 5 6 7
s 1              
a 2              
i 3              
l 4              
n 5              

现在要计算edit(1, 1),而edit(0, 1) + 1 == 2,edit(1, 0) + 1 == 2,edit(0, 0) + f(1, 1) == 0 + 1 == 1,他们其中最小的为1,因此edit(1, 1) == 1。按照此法计算,直到计算到edit(2, 2),矩阵为这样:

  0 f a i l i n g
0 0 1 2 3 4 5 6 7
s 1 1 2 3 4 5 6 7
a 2 2            
i 3              
l 4              
n 5              

这个时候,edit(2, 1) + 1 == 3,edit(1, 2) + 1 == 3,edit(1, 1) + f(2, 2) == 1 + 0 == 1,而此时s1[2] == 'a' 而 s2[1] == 'f'‘,不满足条件,所以,交换相邻字符的操作不纳入比较最小数中计算。接着往下计算,得出最后矩阵为:

  0 f a i l i n g
0 0 1 2 3 4 5 6 7
s 1 1 2 3 4 5 6 7
a 2 2 1 2 3 4 5 6
i 3 3 2 1 2 3 4 5
l 4 4 3 2 1 2 3 4
n 5 5 4 3 2 2 2 3

可以看到edit(len(s1), len(s2)) == 3,验证了先前的结论。

接下来就给出Python实现的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def ed(s1, s2):
     '''
     >>> ed('eeba', 'abac')
     3
     >>> ed('abc', 'cba')
     2
     >>> ed('cbc', 'eba')
     2
     >>> ed('recoginze', 'recognize')
     1
     >>> ed('sailn', 'failing')
     3
     >>> ed('ab', 'ba')
     1
     '''
     # 动态规划求编辑距离
     # param s1: 字符串1
     # param s2: 字符串2
     
     len1 = len (s1)
     len2 = len (s2)
     
     # 初始化矩阵
     matrix = [[i + j for j in range (len2 + 1 )] for i in range (len1 + 1 )]
     
     for row in range (len1):
         for col in range (len2):
             comp = [matrix[row + 1 ][col] + 1 , matrix[row][col + 1 ] + 1 ]
             
             if s1[row] = = s2[col]:
                 comp.append(matrix[row][col])
             else :
                 comp.append(matrix[row][col] + 1 )
             
             # 对相邻字符交换位置的处理判断
             if row > 0 and col > 0 :
                 if s1[row] = = s2[col - 1 ] and s1[row - 1 ] = = s2[col]:
                     comp.append(matrix[row - 1 ][col - 1 ] + 1 )
                     
             matrix[row + 1 ][col + 1 ] = min (comp)
             
     return matrix[len1][len2]

重要的是这段代码:

?
1
2
3
if row > 0 and col > 0 :
     if s1[row] = = s2[col - 1 ] and s1[row - 1 ] = = s2[col]:
         comp.append(matrix[row - 1 ][col - 1 ] + 1 )

同学们要用其他语言实现,只需要实现以上判断,来进行操作(4)。

注意到ed函数的docstring******现了类似命令行的句子,这是为了方便进行doctest测试。要测试全部数据,只需加上以下几句话:

?
1
2
3
if __name__ = = '__main__' :
     import doctest
     doctest.testmod()

分享到

四月23

文章信息

  • Chine
  • 11:27 a.m.
  • 11个评论
  • 持久连接

标签

  • 编辑距离
  • 动态规划

11条留言

  1. kisonlee 说:
    2011年 十一月21日 10:28 p.m.

    你好
    最后的
    if row > 0 and col > 0:
    if s1[row] == s2[col-1] and s1[row-1] == s2[col]:
    comp.append(matrix[row-1][col-1]+1)
    是不是应该改为
    if row > 0 and col > 0:
    if s1[row] == s2[col-1] and s1[row-1] == s2[col]:
    comp.append(matrix[row-2][col-2]+1)

posted on 2013-01-15 23:56  lexus 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lexus/archive/2013/01/15/2862071.html

动态规划求编辑距离 - 残阳似血的博客相关推荐

  1. 波特词干算法 - 残阳似血的博客

    波特词干算法 - 残阳似血的博客 波特词干算法 - 残阳似血的博客 波特词干算法 位于分类 自然语言处理 在英语中,一个单词常常是另一个单词的"变种",如:happy=>ha ...

  2. c语言求n个数的最小值博客,C语言中的#define宏定义 求一组数的最大值和最小值(转)...

    C语言有很多预编译关键字,如#if.#include.#else--,所有以#开头的的关键字都属于这一类,此处讨论#define关键字 #define的核心就是"替换" 如#def ...

  3. 关于Django的网站/博客

    2019独角兽企业重金招聘Python工程师标准>>> 豆瓣小组:Django 海报网 可能是国内最大的 Django 应用网站 果壳网  科技-新知-智趣,很新税的科技主题网站,用 ...

  4. CSDN2013博客之星评选(求投票支持)

    CSDN2013博客之星评选(求投票支持) zouxy09@qq.com http://blog.csdn.net/zouxy09 您好,2013年CSDN博客之星评选开始了.如果我的博客对您有所帮忙 ...

  5. 我参加了51CTO博客大赛,求投票!

    我是张传波,也是Fireball( 火球). 我参加了51 CTO博客 大赛,距离网络投票截止没有几天了,求投票! 我的参赛链接: http://blog.51cto.com/contest2013/ ...

  6. 博客九周年:稳中求胜 持续发展

    本文题图为2014年一位网友给我的题字:不忘初心,现在这幅字我还保留在办公室. 八周年的纪念文章还历历在目呢,当我提笔写下这篇文章的时候,已经是博客九周年的日子了.博客建设的时候,我连对象都没有呢,而 ...

  7. 用matlab的编程法和游动鼠标法求二阶传递函数的上升时间、峰值时间、超调量和调节时间 - Gavin_Hall的博客 - CSDN博客

    1. 准备 终值:c(∞) 上升时间 tr:响应从峰值的10%上升到峰值的90%所需要的时间:而阶跃响应则是从终值的10%上升到终值的90%所需要的时间:对有振荡的系统,也可以定义为从0到第一次到达终 ...

  8. 求csdn博客优良编辑方法

    看见很多大牛的csdn博客编写的非常好,阅读体验也非常强.我就纳闷了,为啥我插公式也不行,插图片也不行呢... 插图片问题:图片不能复制招贴,否则在编辑的时候可以显示但是在发表之后就无法显示了.想要显 ...

  9. 正在参加2021年「博客之星」评选,求投票

    我正在参加年度博客之星评选,请大家帮我投票打分,您的每一分都是对我的支持与鼓励. 麻烦大家点击下面的链接投上5分好评:  https://bbs.csdn.net/topics/603955503

最新文章

  1. 第十五届全国大学生智能汽车竞赛创意组比赛进入全国总决赛队伍名单
  2. Xmpp实现简单聊天系列 --- ②用户注册和登陆
  3. Firefox 插件:鲜味 del.icio.us,和朋友分享你的收藏
  4. java笔记15-日期类
  5. 汇编语言-019(汇编程序与c\c++相互调用)
  6. TASLP | 从判别到生成:基于对比学习的生成式知识抽取方法
  7. Arduino U8glib库中的中文字体
  8. Uniapp 微信小程序登陆页面
  9. iapp进度条倒计时_‎App Store 上的“纪念日提醒 - days matter · 倒数倒计时”
  10. 面试技巧:HR常问的70个问题回答技巧
  11. Linux中 [Error 28] 设备上没有空间
  12. Js逆向:建筑市场监管平台
  13. NCBI:美国国立生物技术信息中心大型数据库
  14. wireshark分析实战
  15. 四川学计算机好的专科学院,四川有哪些好的计算机编程专科学校
  16. Mimicking Very Efficient Network for Object Detection
  17. 酒浓码浓 - node之http
  18. UI一揽子计划 9 (UITableView 、UITableView 、重用机制)
  19. 使用jquery完成元素拖拽效果的实现(鼠标拖动滑块)
  20. Lazarus IDE设置中文

热门文章

  1. js基础及相关面试题
  2. 格子啦越狱助手 v1.0.2 官方版
  3. ubuntu16.04修改ssh端口号
  4. Java实现根据地址调用高德地图获取经纬度等信息
  5. uniapp h5直播拉流的几种方法 flv.js, video.js, 阿里播放器sdk,video标签
  6. 真正的文盲:没有文化的文化人系列(3)
  7. 关于获取excel的日期格式数据问题
  8. BNF 范式(巴科斯范式)简介
  9. 生成DOTA格式的数据集
  10. 深度学习之常见激活函数-Sigmoid、Tanh、ReLu、softplus、softmax、ELU、PReLU