51Nod - 1183 编辑距离(dp)

本文深入解析编辑距离(Levenshtein距离)的概念及其计算方法,通过动态规划算法实现字符串之间的编辑距离计算,提供了详细的步骤说明及示例。

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

描述

编辑距离,又称Levenshtein距离(也叫做Edit
Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
例如将kitten一字转成sitting: sitten (k->s) sittin (e->i) sitting (->g)
所以kitten和sitting的编辑距离是3。俄罗斯科学家Vladimir Levenshtein在1965年提出这个概念。
给出两个字符串a,b,求a和b的编辑距离。

Input

第1行:字符串a(a的长度 <= 1000)。 第2行:字符串b(b的长度 <= 1000)。

Output

输出a和b的编辑距离

Input

kitten
sitting

Output

3

思路

这是一个经典的动态规划问题。设两个字符串为ab,令:

  • dp[i][j]表示a串中[1,i]b串中[1,j]的编辑距离的最优解

那么所有的初始值肯定是dp[i][0]=i,dp[0][j]=j,因为其中有一个为0的时候,不论是添加还是删除,付出的代价就是另一个串的长度。

考虑s1[i]==s2[j],如果这两个字母相等,证明我们可以不用对当前状态进行操作,这个状态可以由dp[i-1][j-1]转移过来,如果不相等,那么付出的代价就是dp[i-1][j-1]+1,我们还需要进行比较第一个串的前一位和当前位置的第二个串,当前串的当前为和第二个串的前一位,因为要进行编辑距离肯定要付出代价1,那么就需要比较:
dp[i-1][j]+1dp[i][j-1]+1dp[i-1][j-1]+same(i,j)这三个值,这也是状态转移方程:dp[i][j]=min(min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+(s1[i-1]==s2[j-1]?0:1))

官方讲解:

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

给定字符串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)。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1000+20;
int dp[N][N];
int get_editdis(string s1,string s2)
{
    int len1=s1.size();
    int len2=s2.size();
    for(int i=0; i<=len1; i++)dp[i][0]=i;
    for(int j=0; j<=len2; j++)dp[0][j]=j;
    for(int i=1; i<=len1; i++)
    {
        for(int j=1; j<=len2; j++)
        {
            dp[i][j]=min(min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+(s1[i-1]==s2[j-1]?0:1));
        }
    }
    return dp[len1][len2];
}
int main()
{
    string s1,s2;
    cin>>s1>>s2;
    cout<<get_editdis(s1,s2)<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值