几种距离的基础研究上——编辑距离

一、编辑距离(Levenshtein距离)

简介

这个距离是1965年一个战斗名族叫Levenshtein的一个发明的一个算法。

这个所谓的距离,实际是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。这些编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。

维基百科

中文维基:快戳我

英文维基:英文不好别戳我

基本原理

算法就是简单的线性动态规划(最长上升子序列就属于线性动态规划)。

设我们要将s1变成s2

定义状态矩阵edit[len1][len2],len1和len2分别是要比较的字符串s1和字符串s2的长度+1(+1是考虑到动归中,一个串为空的情况)

然后,定义edit[i][j]是s1中前i个字符组成的串,和s2中前j个字符组成的串的编辑距离

具体思想是,对于每个i,j从0开始依次递增,对于每一次j++,由于前j-1个字符跟i的编辑距离已经求出,所以只用考虑新加进来的第j个字符即可

插入操作:在s1的前i个字符后插入一个字符ch,使得ch等于新加入的s2[j]。于是插入字符ch的编辑距离就是edit[i][j-1]+1

删除操作:删除s1[i],以期望s1[i-1]能与s2[j]匹配(如果s1[i-1]前边的几个字符能与s2[j]前边的几个字符有较好的匹配,那么这么做就能得到更好的结果)。另外,对于s1[i-1]之前的字符跟s2[j]匹配的情况,edit[i-1][j]中已经考虑过。于是删除字符ch的编辑距离就是edit[i-1][j]+1

替换操作:期望s1[i]与s2[j]匹配,或者将s1[i]替换成s2[j]后匹配。于是替换操作的编辑距离就是edit[i-1][j-1]+f(i,j)。其中,当s1[i]==s2[j]时,f(i,j)为0;反之为1

于是动态规划公式如下:

if i == 0 且 j == 0,edit(i, j) = 0

if i == 0 且 j > 0,edit(i, j) = j

if i > 0 且j == 0,edit(i, j) = i

if 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) },当第一个字符串的第i个字符不等于第二个字符串的第j个字符时,f(i, j) = 1;否则,f(i, j) = 0。


上面的描述并不形象,换个图形化表示:

下图为计算字符“beauty”与“batyu”编辑距离的二维图解:

图1

图2

步骤详解:

几点说明:

(1) 坐标轴垂直向下为x轴正向,水平向右为y轴正向。因此红圈1的坐标为(1,1),红圈2的坐标为(1,2)。

(2) 这个矩阵中的点含义是两个字符要经过多少编辑操作才能完成转换。编辑操作就是上面的3类操作。

(3) 在更新矩阵中距离的时候,对应参照的向量中表示左侧(相当于上面讲到的删除操作)和上测(相当于上面讲到的插入操作)的值,不论它对应的字母是否相同,左侧和上测向量值均加1。

推算步骤:

(1) 先建立一张二维表(矩阵),如上图所示。矩阵中(0,0)位置不对应字母。

(2) 计算矩阵(1,1)位置(即:红圈1所在的置)的值,此处为方便描述,将该位置定义为A点。

A点的值需要由A点左上方、左边和上边的值共同决定。为方便描述先将A点所需要的三个值赋给向量a,则a=(0,1,1)。A点对应的字母分别为(b,b),字母相同,则A点左上角的值加0(不同则加1),A点左边与上边的值分别加1。此时a=(0,2,2),取a中做小值填入A点位置,见右图所示。

计算矩阵(1,2)位置(即:红圈2所在的位置),定义为B点。B点对应向量为b=(1,0,2)。由于B点对应的字母为(b,e),字母不同,则B点左上角的值加1,同时,B点左侧上侧分别加1。此时b=(2,1,3),取b中最小值赋给B点。

(3) 按照步骤2)求出每个格子中的值。所有值求出后,右下角的值为最小编辑距离。

Python实现

这里给出了一个别人实现的代码,同时,有人指出了它的缺陷,再此进行了修正。

#!/user/bin/env python  
# -*- coding: utf-8 -*-  
  
class Levenshtein(object):  
      
    def __init__(self):  
        pass
     
    def get_distance(self,first,second):  
        """编辑距离的求算过程。"""
        
        # 如果字符串1的长度大于字符串2的长度,则交换两个字符串。
        if len(first) > len(second):  
            first,second = second,first
        # 如果两个字符串中有一个是空串,则另一个串可以通过字符串长度转换为该串(删除或者插入字符)。
        if len(first) == 0:  
            return len(second)  
        if len(second) == 0:  
            return len(first)  
        
        first_length = len(first) + 1  
        second_length = len(second) + 1 
        
        # 初始化基础字符串
        distance_matrix = [[0 for y in range(second_length)] for x in range(first_length)]
        # 初始化x轴初值
        for x in range(first_length):
            distance_matrix[x][0] = x
        # 初始化y轴初值
        for y in range(second_length):
            distance_matrix[0][y] = y
        
        # 开始计算距离 
        for i in range(1,first_length):  
            for j in range(1,second_length):  
                del_edtion = distance_matrix[i-1][j] + 1  
                ins_edtion = distance_matrix[i][j-1] + 1  
                sub_edtion = distance_matrix[i-1][j-1]  
                if first[i-1] != second[j-1]:  
                    sub_edtion += 1  
                distance_matrix[i][j] = min(ins_edtion,del_edtion,sub_edtion)   
        return distance_matrix[first_length-1][second_length-1]  
      
if __name__ == "__main__":  
    leven_obj = Levenshtein()  
    print leven_obj.get_distance('beauty','batyu')

结果矩阵

[0, 1, 2, 3, 4, 5, 6]
[1, 0, 1, 2, 3, 4, 5]
[2, 1, 1, 1, 2, 3, 4]
[3, 2, 2, 2, 2, 2, 3]
[4, 3, 3, 3, 3, 3, 2]
[5, 4, 4, 4, 3, 4, 3]

参考文献(排名不分先后)

  1. "使用最小编辑距离算法求字符串相似度"
  2. "最小编辑距离_Python"
  3. "字符串相似性算法【最长公共字符串算法】 【LCS】"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,724评论 0 33
  • 分治方法 将问题划分成互不相交的子问题 递归地求解子问题 将子问题的解组合起来 动态规划(两个要素:最优子结构、子...
    superlj666阅读 492评论 0 0
  • 动态规划(Dynamic Programming) 本文包括: 动态规划定义 状态转移方程 动态规划算法步骤 最长...
    廖少少阅读 3,256评论 0 18
  • thiele插值算法 1点插值算法 function [C,c]=thiele(X,Y,Z)%X为插值点横坐标,Y...
    00crazy00阅读 1,973评论 0 4
  • 无意间看到了有人问编辑距离算法,当时对这个概念很陌生,也就去学习了下,做下总结,记录下,好记性不如烂笔头。 编辑距...
    辣条阅读 3,937评论 0 0