基本介绍

  Levenshtein距离是一种计算两个字符串间的差异程度的字符串度量(string metric)。我们可以认为Levenshtein距离就是从一个字符串修改到另一个字符串时,其中编辑单个字符(比如修改、插入、删除)所需要的最少次数。俄罗斯科学家Vladimir Levenshtein于1965年提出了这一概念。

简单例子

  从字符串“kitten”修改为字符串“sitting”只需3次单字符编辑操作,如下:

    • sitten ( k -> s )
    • sittin ( e -> i )
    • sitting ( _ -> g )

  因此“kitten”和“sitting”的Levenshtein距离为3。

实现思想

  如何编程实现这一算法呢?许多人试图用矩阵来解释,但实际上矩阵是最终可视化的工具,配合理解“为什么”比较方便,但从矩阵却比较难想到“怎么做”。

  我们试图找到“从字符串$A$修改到字符串$B$”这一问题的子解结构。当然反过来说“从字符串$B$修改到字符串$A$”和它是同一个问题,因为从$A$中删掉一个字符来匹配$B$,就相当于在$B$中插入一个字符来匹配$A$,这两个操作是可以互相转化的。

  假设字符序列$A[1\ldots i]$、$B[1\ldots j]$分别是字符串$A$、$B$的前$i$、$j$个字符构成的子串,我们得到一个子问题是“从字符串$A[1\ldots i]$修改到字符串$B[1\ldots j]$”:$$\left[\begin{matrix}\begin{aligned}&A:&&A[1]&&A[2]&&\cdots&&A[i-2]&&A[i-1]&&A[i]\\\\&B:&&B[1]&&B[2]&&\cdots&&B[j-2]&&B[j-1]&&B[j]\end{aligned}\end{matrix}\right]$$

  ① 插入操作

    • 当将$A[1\ldots i]$修改成$B[1\ldots j-1]$需要操作数为$op_1$,那么我插入一个字符$A[i']=B[j]$到$A[i]$和$A[i+1]$之间,用以匹配$B[j]$,于是$A[1\ldots i]$修改到$B[1\ldots j]$所需操作数为$op_1+1$。$$\left[\begin{matrix}\begin{aligned}&&\cdots&&\color{Red}{A[i-2]}&&\color{Red}{A[i-1]}&&\mathbf{\color{Red}{A[i]}}&&\mathbf{\color{Blue}{A[i']}}&&\\\\&&\cdots&&\color{Red}{B[j-2]}&&\mathbf{\color{Red}{B[j-1]}}&&\mathbf{\color{Blue}{B[j]}}&&\phi&&\end{aligned}\end{matrix}\right]$$

  ② 删除操作

    • 当将$A[1\ldots i-1]$修改成$B[1\ldots j]$需要操作数为$op_2$,那么我删掉字符$A[i]$也可以$op_2+1$的操作数使两个子字符串匹配:$$\left[\begin{matrix}\begin{aligned}&&\cdots&&\color{Red}{A[i-2]}&&\mathbf{\color{Red}{A[i-1]}}&&\mathbf{\color{Blue}{\phi}}&&\\\\&&\cdots&&\color{Red}{B[j-2]}&&\color{Red}{B[j-1]}&&\mathbf{\color{Red}{B[j]}}&&\end{aligned}\end{matrix}\right]$$

  ③ 修改操作

    • 如果$A[1\ldots i-1]$修改成$B[1\ldots j-1]$所需操作数为$op_3$的话,我将字符$A[i]$替换成$A[i']=B[j]$,就可以$op_3+1$的操作数完成:$$\left[\begin{matrix}\begin{aligned}&&\cdots&&\color{Red}{A[i-2]}&&\mathbf{\color{Red}{A[i-1]}}&&\mathbf{\color{Blue}{A[i']}}&&\\\\&&\cdots&&\color{Red}{B[j-2]}&&\mathbf{\color{Red}{B[j-1]}}&&\mathbf{\color{Blue}{B[j]}}&&\end{aligned}\end{matrix}\right]$$
    • 但如果此时字符$A[i]==B[j]$的话,则不需要进行修改操作,操作数仍为$op_3$。

  综上所述,我们将字符串$A[1\ldots i]$修改成字符串$B[1\ldots j]$所需操作为$min\{op_1+1,\ op_2+1,\ op_3+1_{(a_i\neq b_i)}\}$,其中$1_{(a_i\neq b_i)}$代表当$a_i\neq b_i$时取值$1$,否则取值为$0$。

数学定义

  数学上,我们定义两个字符串$A$和$B$间的Levenshtein距离为$lev_{A,\ B}(a,\ b)$,其中$a$、$b$分别为字符串$A$、$B$的长度,而$$lev_{A,\ B}(i,\ j)=\left\{\begin{matrix}\begin{aligned}&i&&,\ j=0\\&j&&,\ i=0\\&min\left\{\begin{matrix}lev_{a,\ b}(i,\ j-1)+1\\lev_{a,\ b}(i-1,\ j)+1\\lev_{a,\ b}(i-1,\ j-1)+1_{(a_i\neq b_i)}\end{matrix}\right.&&,\ otherwise\end{aligned}\end{matrix}\right.$$

  更多请参考 Wikipedia - Levenshtein_distance

C++代码

  有了状态转移方程,我们就可以愉快地DP了,时间复杂度$O(MN)$,空间复杂度$O(MN)$。

 #include <stdio.h>
#include <string.h>
#include <algorithm>
using std::min;
int lena, lenb;
char a[], b[];
void read() {
scanf("%s%s", a, b);
lena = strlen(a);
lenb = strlen(b);
} int dp[][];
void work() {
for(int i=; i<=lena; i++) dp[i][] = i;
for(int j=; j<=lenb; j++) dp[][j] = j;
for(int i=; i<=lena; i++)
for(int j=; j<=lenb; j++)
if(a[i-]==b[j-])
dp[i][j] = dp[i-][j-];
else
dp[i][j] = min(dp[i-][j-], min(dp[i][j-], dp[i-][j]))+;
printf("%d\n", dp[lena][lenb]);
} int main() {
read();
work();
return ;
}

几个小优化

  1. 如果满足$A[i]==B[j]$(下标从$1$开始),实际上是可以直接取$lev(i,\ j)=lev(i-1,\ j-1)$的。因为此时字符相同是不需要任何编辑操作的。这一优化也可以从上文转移方程中构造不等关系得出。

  2. 如果使用滚动数组,则空间复杂度可以降到$O(2*max\{M,\ N\})$。但也可以通过保存$lev(i-1,\ j-1)$来把空间复杂度降到$O(max\{M,\ N\})$,如下:

 int dp[];
void work() {
for(int j=; j<=lenb; j++) dp[j] = j;
int t1, t2;
for(int i=; i<=lena; i++) {
t1 = dp[]++;
for(int j=; j<=lenb; j++) {
t2 = dp[j];
if(a[i-]==b[j-])
dp[j] = t1;
else
dp[j] = min(t1, min(dp[j-], dp[j]))+;
t1 = t2;
}
}
printf("%d\n", dp[lenb]);
}

以上即为Levenshtein距离算法的基本介绍,如果您喜欢,请点个推荐吧~如果您有宝贵意见,欢迎在下方评论区提出哦~

本文基于知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议发布,欢迎引用、转载或演绎,但是必须保留本文的署名BlackStorm以及本文链接http://www.cnblogs.com/BlackStorm/p/5400809.html,且未经许可不能用于商业目的。如有疑问或授权协商请与我联系

字符串编辑距离(Levenshtein距离)算法的更多相关文章

  1. Levenshtein Distance莱文斯坦距离算法来计算字符串的相似度

    Levenshtein Distance莱文斯坦距离定义: 数学上,两个字符串a.b之间的莱文斯坦距离表示为levab(|a|, |b|). levab(i, j) = max(i, j)  如果mi ...

  2. Levenshtein字符串距离算法介绍

    Levenshtein字符串距离算法介绍 文/开发部 Dimmacro KMP完全匹配算法和 Levenshtein相似度匹配算法是模糊查找匹配字符串中最经典的算法,配合近期技术栏目关于算法的探讨,上 ...

  3. Levenshtein Distance算法(编辑距离算法)

    编辑距离 编辑距离(Edit Distance),又称Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数.许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符, ...

  4. 字符串相似度算法——Levenshtein Distance算法

    Levenshtein Distance 算法,又叫 Edit Distance 算法,是指两个字符串之间,由一个转成另一个所需的最少编辑操作次数.许可的编辑操作包括将一个字符替换成另一个字符,插入一 ...

  5. 字符串相似度算法-LEVENSHTEIN DISTANCE算法

    Levenshtein Distance 算法,又叫 Edit Distance 算法,是指两个字符串之间,由一个转成另一个所需的最少编辑操作次数.许可的编辑操作包括将一个字符替换成另一个字符,插入一 ...

  6. 51nod 1183 - 编辑距离 - [简单DP][编辑距离问题][Levenshtein距离问题]

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1183 编辑距离,又称Levenshtein距离(也叫做Edi ...

  7. 动态规划 001 - 编辑距离(Levenshtein Distance)问题

    问题 字符串的编辑距离也被称为距Levenshtein距离(Levenshtein Distance),属于经典算法,常用方法使用递归,更好的方法是使用动态规划算法,以避免出现重叠子问题的反复计算,减 ...

  8. 字符串编辑距离(Edit Distance)

    一.问题描述定义字符串编辑距离(Edit Distance),是俄罗斯科学家 Vladimir Levenshtein 在 1965 年提出的概念,又称 Levenshtein 距离,是指两个字符串之 ...

  9. 编辑距离及其动态规划算法(Java代码)

    编辑距离概念描述 编辑距离,又称Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数.一般情况下编辑操作包括: 将一个字符替换成另一个字符: 插入一个字符: 删除一个字 ...

随机推荐

  1. 设计模式(八): 从“小弟”中来类比"外观模式"(Facade Pattern)

    在此先容我拿“小弟”这个词来扯一下淡.什么是小弟呢,所谓小弟就是可以帮你做一些琐碎的事情,在此我们就拿“小弟”来类比“外观模式”.在上面一篇博文我们完整的介绍了“适配器模式”,接下来我们将要在这篇博客 ...

  2. JSON(及其在ajax前后端交互的过程)小识

    一. json介绍 json是一种轻量级的数据交换格式,规则很简单: 并列的数据之间用逗号(,)分隔: 映射用冒号(:)表示: 并列数据的集合(数组)用方括号([])表示: 映射的集合(对象)用大括号 ...

  3. (转)ubuntu apache2 的负载均衡和反向代理

    apache功能其实很是强大,最近一段时间研究了下apache的反向代理和负载均衡,反向代理网协的外网出口和我的博客出口就是通过apache的反向代理实现的,总结一下,重点说说负载均衡的配置. 首先说 ...

  4. Error:const char* 类型的实参和LPCWSTR类型的形参不兼容的解决方法。

    在C++的Windows 应用程序中经常碰到这种情况. 解决方法: 加入如下转换函数: LPCWSTR stringToLPCWSTR(std::string orig) { size_t origs ...

  5. 匹夫细说C#:从园友留言到动手实现C#虚函数机制

    前言 上一篇文章匹夫通过CIL代码简析了一下C#函数调用的话题.虽然点击进来的童鞋并不如匹夫预料的那么多,但也还是有一些挺有质量的来自园友的回复.这不,就有一个园友提出了这样一个代码,这段代码如果被编 ...

  6. JavaScript : 浅讲ajax

    1.ajax入门案例 1.1 搭建Web环境 ajax对于各位来说,应该都不陌生,正因为ajax的产生,导致前台页面和服务器之间的数据传输变得非常容易,同时还可以实现页面的局部刷新.通过在后台与服务器 ...

  7. 谈谈React那些小事

    前言 说起React,那也是近一年多时间火起来的前端框架,其在Facebook的影响力和大力推广下,已然成为目前前端界的中流砥柱.在如今的前端框架界,React.Vue.Angular三分天下的时代已 ...

  8. macOS 升级到了10.12.1

    除了明面上的一些更新,但我感觉最重要的是触摸板的行为特征又还原了.

  9. 《连载 | 物联网框架ServerSuperIO教程》- 3.设备驱动介绍

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  10. 使用Maven私服的好处

    1.Maven仓库的分类 本地仓库:当Maven执行编译或测试时,如果需要使用到依赖文件,它总是基于坐标使用本地仓库的依赖文件.默认情况下,不管Linux还是Windows,每个用户在自己的用户目录下 ...