levenshtein字符串编辑距离算法

本文深入讲解了Levenshtein编辑距离算法,介绍了如何通过插入、删除及替换操作将一个字符串转换为另一个字符串所需的最小步骤,并提供了PHP实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

字符串编辑距离

指的是字符串A向字符串B转换的最小步数。比如字符串“ABC”转换成“A”最少需要删除“B”,“C”两个字符。字符串操作有三种,一个是新增插入,一个是删除,一个是替换。该算法最早由 levenshtein提出。

从A字符串向B字符串转换,最重要的是考虑不要重复操作,比如 “abd”转换成“abcd”只需要插入一个“c”,而不用删去“d”再插入“cd”。这就要看Levenshtein Distance算法的思想了。

算法基本原理:假设我们可以使用d[ i , j ]个步骤,表示将串s[ 1…i ] 转换为串t [ 1…j ]所需要的最少步骤个数,在最基本的情况下,即在i等于0时,也就是说串s为空,那么对应的d[0,j] 就是至少增加j个字符,使得s转化为t。在j等于0时,也就是说串t为空,那么对应的d[i,0] 就是减少i个字符,使得s转化为t。

原理看起来复杂,其实就是规定了一个二维数组 d。d[i][j]表示长度为i的字符串S向长度为j的字符串T的编辑距离。那么我们知道,比如说字符串S =“abceh“要编辑成字符串T=“bbeh”,为了下标对应,我们可以设置一个二维数组d[5+1][4+1]。根据我们的设定,d[0][0]表示的就是长度为0的S(即为空)转换成长度为0的T(也为空),需要0步操作。表格如下:

这里写图片描述

黄色表格就是d[0][0]的值,橙色的是d[0][1]的值,表示的就是长度为0的S(即为空)转换成长度为1的T(即“b”),我们初始化的时候可以轻易算出表格上方,左方的所有步数。我们现在开始求d[1][1](表格中?问号)的值,它表示的是长度为1的字符串S(即“a”)编辑成长度为1的字符串T(即“b“)的距离。我们可以轻易知道这个值最小是1,操作是替换,既不是新增插入,也不是删除。于是表格便成了:

这里写图片描述

我们的继续求下一个问号所在的步数d[1][2]。我们为什么要求这个从“a”转换成“bb”的步数呢?原因就是为了比较字符编辑时候,插入、删除、替换哪个操作是最优操作。比如这里,”a”转换成“bb”,刚刚我们已经求出了“a”转换成“b”的最短编辑距离是1(也就是d[1][1]),因此现在变成了求“b”到“bb”的最短编辑距离,因为字符串“a”在d[1][1]的时候已经被我们转换成“b”了。因此d[1][2]的值应该为d[1][1]+1。我们以此类推添加表格的第一行。

这里写图片描述

求第二行d[2][1](也就是“ab”=>“b“)的时候,这里有一个注意的地方,就是对于“ab”=>“b”的情况,Levenshtein 是使用替换而不是删除操作,也就是把“a”替换成空字符。所以这里我们对字符“ab”的最优操作是把“a”替换成空字符,再拿“ b”与“b”比较。也就是d[2][1] = d[1][0]+(S[2-1]==T[1-1]?0:1) 这里S,T的下标记得-1,因为是取数组下标。所以我们得出结论:

  1. d[m][n]步如果是新增,那么d[m][n] = d[m][n-1]+1。
  2. d[m][n]步如果是删除,那么d[m][n] = d[m-1][n]+1。
  3. d[m][n]步如果是替换,那么d[m][n] = d[m-1][n-1]+ (S[m -1] == T[n -1]?0:1)。
  4. d[i][j]的最优解就是它上一步的最优解加上上面三步的最小值。

Levenshtein Distance算法的用最优解累加的方法求出最优编辑距离。这也是一种穷举的办法。PHP中就有levenshtein函数。我们现在自己实现一下这个算法的代码:

function countDistance($s,$t){
        $lens = strlen($s);
        $lent = strlen($t);
        if($lens == 0 ){
            return $lent;
        }
        if($lent == 0){
            return $lens;
        }
        $dp = array();
        for($i = 0; $i <= $lens; $i++){
            //初始化第0列
            $dp[$i][0] = $i;
        }
        for($j = 0;$j<= $lent;$j++){
            //初始化第0行
            $dp[0][$j] = $j;
        }

        for($i = 1;$i<= $lens;$i++){
            for($j =1;$j <= $lent;$j ++){
                //插入
                $insert = $dp[$i][$j-1] + 1;
                //删除
                $del = $dp[$i-1][$j]+1;
                //替换
                $modi = $dp[$i-1][$j-1] + ($s[$i -1] == $t[$j -1]?0:1);                
                $dp[$i][$j] = min($insert,$del,$modi);
            }
        }
        return $dp[$lens][$lent];
    }

我们上面简单的例子基本上实现了Levenshtein算法,当然还有两个缺点:

  • 中文字符等操作的情况。
  • 二维数组很占空间,比如2个1000字符的字符串,所需要的二维数组很庞大。需要改写。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值