动态规划(3)最短编辑距离

来源:互联网 发布:会计网络教育 编辑:程序博客网 时间:2024/06/17 14:13

编辑距离,又称Levenshtein距离(俄罗斯科学家Vladimir Levenshtein在1965年提出这个概念),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。刚开始接触到这个概念是研一的时候看研三师兄写的论文,基于这个编辑距离的改进版本算法的,投的英文版的期刊,也是用动态规划做的,大家可以google学术搜下改进版本算法,这里分享下曹神的算法教程来介绍下原始算法。ok,言归正传,这里的编辑操作是对给定两个字符串S和T:

(1) 在任意位置添加任意字符
(2) 删除存在的任意字符
(3) 修改任意字符

问最少操作多少次可以把字符串T变成S?

例如: S= “ABCF” T = “DBFG”

那么我们可以

(1) 把D改为A
(2) 删掉G
(3) 加入C

所以答案是3。

这个问题之所以难,是难在有“添加”“删除”这样的操作,很麻烦。我们试试换个角度理解问题,把它看成字符串对齐的问题,事实上从生物信息学对比基因的角度,我们可以这样理解问题。

给定字符串S和T,我们可以用一种特殊字符促成两个字符串的对齐。我们加的特殊字符是“-”, 我们允许在S和T中任意添加这种特殊字符使得它长度相同,然后让这两个串“对齐”,最终两个串相同位置出现了不同字符,就扣1分,我们要使得这两个串对齐扣分尽量少。

对于例子我们实际上采取了这样的对齐方式:

12345
ABCF-
DB-FG

注意:如果要对齐,两个“-”相对是没有意义的,所以我们要求不出现这种情况。
那么看一下:
(1) S,T对应位置都是普通字符,相同,则不扣分。 例如位置2,4
(2) S,T对应位置都是普通字符,不同,则扣1分。 例如位置1
(3) S在该位置是特殊字符,T在该位置是普通字符,则扣1分,例如位置5
(4) S在该位置是普通字符,T在该位置是特殊字符,则扣1分,例如位置3

我们来看看扣分项目对应什么?

(1) 不扣分,直接对应
(2) 对应把T中对应位置的字符修改
(3) 对应在T中删除该字符
(4) 对应在T中添加该字符

好了,目标明确,感觉像不像 LCS?我们尝试一下:设f(i,j)表示S的前i位和T的前j位对齐后的最少扣分。

那我们来看看最后一位,对齐的情况

(1) 必须S[i] == T[j], 这时前i – 1和j – 1位都已经对齐了,这部分肯定要最少扣分。这种情况下最少的扣分是f(i-1,j-1)
(2) 和(1)类似,S[i]≠T[j],这种情况下最少的扣分是f(i -1, j – 1) + 1
(3) S的前i位和T的前(j – 1)位已经对齐了,这部分扣分也要最少。这种情况下最少的扣分是f(i,j-1) + 1
(4) S的前(i-1)位已经和T的前j位对齐了,这部分扣分要最少。这种情况下最少的扣分是f(i,j-1) + 1

具体f(i,j)取什么值,显然是要看哪种情况的扣分最少。

为了方便,我们定义函数same(i,j)表示如果S[i] == T[j]则为0,否则为1。

我们来表示一下递推式:

f(i,j) = min(f(i – 1, j – 1) + same(i,j), f(i – 1,j ) + 1, f(i, j – 1) + 1)

初值是什么?

f(0, j) = j
f(i, 0) = i

这时因为对于S的前0位,我们只能在之前加入“-”,或者说把T全部删掉了。类似地,对于T地前0位,我们只能把S的字符都加进来,别无选择。
注意上述两个式子的重合点 f(0,0) = 0也符合我们的定义,并不矛盾。

时间复杂度? O(m * n),空间复杂度? O(m * n)。同样我们发现到f(i,j)只与本行和上一行有关,可以省掉一维的空间复杂度,从而达到O(n)。

上代码:

a=input()b=input()m=len(a)n=len(b)f=[]ans=[]for j in range(n+1):    f.append(j)#0行n列,当a长度为0时b需要删除的次数for i in range(1,m+1):    last=i-1    f[0]=i    for j in range(1,n+1):        up=f[j]        same=a[i-1]!=b[j-1]        f[j]=min(last+same,up+1,f[j-1]+1)        last=upprint(f[-1])

对a=’kitten’,b=’sitting’,很容易按照以上算法得到编辑距离为3,那如何得到具体编辑动作呢?
实际上我们可以和LCS算法一样回溯,回溯方法很简单:从最后一行最后一列的元素开始遍历,这个元素实际上就是最小编辑距离,如果左或者上或者左斜上元素(a或b或c)存在的话,指向min(a,b,c)回溯,直到这个元素变成1为止。见下图:
3.1

具体编辑操作和指向方向有关,向左是删除,向上是添加,左斜上是更新。回溯代码就不写了,跟LCS很类似了。

0 0
原创粉丝点击