Edit Distance(编辑距离)

作者: nlpjoe | 来源:发表于2018-01-28 18:04 被阅读55次

    前言

    今天看了Stanford编辑距离代码,感觉写得不错,写一篇博客记录下。

    编辑距离的定义是:从字符串A到字符串B,中间需要的最少操作权重。这里的操作权重一般是:

    • 删除一个字符(deletion)
    • 插入一个字符(insertion)
    • 替换一个字符(substitution)
    • 他们的权重都是1

    编辑距离的算法一般用dp。很多博客写到这里就结束了,因此十分晦涩难懂。因为没有对其加主谓语,完全就是耍流氓。正确的说法应该是:

    • 删除A末尾一个字符(deletion)
    • 用B末尾插入A末尾一个字符(insertion)
    • 把A末尾字符替换成B末尾的一个字符(substitution)

    为什么?

    算法及实现

    我们举一个实际例子

    • 长度为m的字符串A,len(A) = m
    • 长度为n的字符串B,len(B) = n

    则A到B的编辑距离dp公式如下:

    编辑距离DP公式

    先不要急着看懂,我慢慢解释。

    • Q2: 为什么d是一个[m+1][n+1]大小的二维数组,为什么d数组要比字符串长度大一?

    • A2: 考虑A、B都为空字符串,我们还是需要一个[1][1]大小的数组记录其编辑距离为0。更进一步也就是说,我们假设字符串A为"AC",则我们需要考虑['', 'A', 'AC']三种情况。

    • Q1: 如何理解d[i][j]的计算公式?

    • A1: 第(i,j)个位置的计算需要依赖于和它相邻的三个元素(i-1,j)、(i, j-1)和(i-1,j-1),关键是哪一个对应删除,哪一个对应于插入,哪一个对应于替换?如果此时A[i]不等于B[j],则(下面为全文最重要部分):

      • 对于(i-1,j-1)时,d(i-1, j-1)表示完成从A[0,i-1]到B[0,j-1]的编辑次数,即现在A[0,i-1]=B[0,j-1],对于(i,j),我们直接把A[i]替换成B[j]即完成编辑。因此(i-1,j-1)对应于把A[i]用B[j]替换的一次操作
      • 对于(i-1, j)时,d(i-1, j)表示完成从A[0, i-1]到B[0, j]的编辑次数,即现在A[0,i-1]=B[0,j],对于(i,j),我们直接把A[i]删除即可完成编辑,因此(i-1,j)对应于把A[i]删除的一次操作
      • 对于(i, j-1)时,d(i, j-1)表示完成从A[0, i]到B[0, j-1]的编辑次数,即现在A[0,i]=B[0,j-1],对于(i,j),我们直接用B[j]插入到A[i]的位置即可完成编辑,因此(i,j-1)对应于把B[j]插到A[i]的一次操作

    理解了上面的文字就理解编辑距离DP算法了,写得有点冗长。

    这里给一个带Damerau–Levenshteindistance距离的代码,其中添加了一种操作:

    • 置换两个字符(transposition),也就是说'ab'到'ba'的操作消耗值为1

    代码地址:https://gist.github.com/nlpjoe

    核心部分为score_edit_distance(self, source, target):

    def score_edit_distance(self, source, target):
       if source == target:
           return 0
       s_pos = len(source)
       t_pos = len(target)
       self.clear(s_pos, t_pos)
       for i in range(s_pos + 1):
           for j in range(t_pos + 1):
               b_score = self.score[i][j]
               if b_score != self.worse():
                   continue
               if i == 0 and j == 0:  # 0,0位置为空,默认为正确
                   b_score = self.best()
               else:
                   if i > 0:  # 删除权重
                       b_score = min(b_score, self.score[i-1][j] + self.delete_cost(source[i-1]))
                   if j > 0:  # 插入权重
                       b_score = min(b_score, self.score[i][j-1] + self.insert_cost(target[j-1]))
                   if i > 0 and j > 0:  # 替换权重
                       b_score = min(b_score, self.score[i-1][j-1] + self.substitute_cost(source[i-1], target[j-1]))
                   if i > 1 and j > 1:  # 置换权重
                       b_score = min(b_score, self.score[i-2][j-2] + self.transpose_cost(source[i-2], source[i-1], target[j-2], target[j-1]))
               self.score[i][j] = b_score
       return self.score[s_pos][t_pos]
    

    输出结果为:

    0
    5.0
    1.0

    相关文章

      网友评论

        本文标题:Edit Distance(编辑距离)

        本文链接:https://www.haomeiwen.com/subject/qzjzaxtx.html