编辑距离 百科内容来自于: 百度百科

应用

最小编辑距离通常作为一种相似度计算函数被用于多种实际应用中,详细如下: (特别的,对于中文自然语言处理,一般以词为基本处理单元)
  • DNA分析:基因学的一个主要主题就是比较 DNA 序列并尝试找出两个序列的公共部分。如果两个 DNA 序列有类似的公共子序列,那么这些两个序列很可能是同源。在比对两个序列时,不仅要考虑完全匹配的字符,还要考虑一个序列中的空格或间隙(或者,相反地,要考虑另一个序列中的插入部分)和不匹配,这两个方面都可能意味着突变(mutation)。在序列比对中,需要找到最优的比对(最优比对大致是指要将匹配的数量最大化,将空格和不匹配的数量最小化)。如果要更正式些,可以确定一个分数,为匹配的字符添加分数、为空格和不匹配的字符减去分数。
全局序列比对尝试找到两个完整的序列 S1S2之间的最佳比对。以下面两个 DNA 序列为例:
S1= GCCCTAGCG
S2= GCGCAATG
如果为每个匹配字符一分,一个空格扣两分,一个不匹配字符扣一分,那么下面的比对就是全局最优比对:
S1'= GCCCTAGCG
S2'= GCGC-AATG
连字符(-)代表空格。在 S2'中有五个匹配字符,一个空格(或者反过来说,在 S1'中有一个插入项),有三个不匹配字符。这样得到的分数是 (5 * 1) + (1 * -2) + (3 * -1) = 0,这是能够实现的最佳结果。
使用 局部序列比对,不必对两个完整的序列进行比对,可以在每个序列中使用某些部分来获得最大得分。使用同样的序列 S1S2,以及同样的得分方案,可以得到以下局部最优比对 S1''S2''
S1 = GCCCTAGCG
S1''= GCG
S2''= GCG
S2 = GCGCAATG
这个局部比对的得分是 (3 * 1) + (0 * -2) + (0 * -1) = 3。
  • 拼写纠错(Spell Correction):又拼写检查(Spell Checker),将每个词与词典中的词条比较,英文单词往往需要做词干提取等规范化处理,如果一个词在词典中不存在,就被认为是一个错误,然后试图提示N个最可能要输入的词——拼写建议。常用的提示单词的算法就是列出词典中与原词具有最小编辑距离的词条。
这里肯定有人有疑问:对每个不在词典中的词(假如长度为len)都与词典中的词条计算最小编辑距离,时间复杂度是不是太高了?的确,所以一般需要加一些剪枝策略,如:
  1. 因为一般拼写检查应用只需要给出Top-N的纠正建议即可(N一般取10),那么我们可以从词典中按照长度依次为len、len-1、len+1、len-2、len-3、...的词条比较;
  2. 限定拼写建议词条与当前词条的最小编辑距离不能大于某个阈值;
  3. 如果最小编辑距离为1的候选词条超过N后,终止处理;
  4. 缓存常见的拼写错误和建议,提高性能。
  • 命名实体抽取(Named Entity Extraction):由于实体的命名往往没有规律,如品牌名,且可能存在多种变形、拼写形式,如“IBM”和“IBM Inc.”,这样导致基于词典完全匹配的命名实体识别方法召回率较低,为此,我们可以使用编辑距离由完全匹配泛化到模糊匹配,先抽取实体名候选词。
具体的,可以将候选文本串与词典中的每个实体名进行编辑距离计算,当发现文本中的某一字符串的编辑距离值小于给定阈值时,将其作为实体名候选词;获取实体名候选词后,根据所处上下文使用启发式规则或者分类的方法判定候选词是否的确为实体名。
  • 实体共指(Entity Coreference ):通过计算任意两个实体名之间的最小编辑距离判定是否存在共指关系?如“IBM”和“IBM Inc.”, "Stanford President John Hennessy "和"Stanford University President John Hennessy"。
  • 机器翻译(Machine Translation):
  1. 识别平行网页对:由于平行网页通常有相同或类似的界面结构,因此平行网页在HTML结构上应该有很大近似度。首先将网页的HTML标签抽取出来,连接成一个字符串,然后用最小编辑距离考察两个字符串的近似度。实际中,此策略一般与文档长度比例、句对齐翻译模型等方法结合使用,以识别最终的平行网页对。
  2. 自动评测:首先存储机器翻译原文和不同质量级别的多个参考译文,评测时把自动翻译的译文对应到与其编辑距离最小的参考译文上,间接估算自动译文的质量,如下图所示:
  • 字符串核函数(String Kernel):最小编辑距离作为字符串之间的相似度计算函数,用作核函数,集成在SVM中使用。

算法

自然语言表达

比如要计算cafe和coffee的编辑距离。cafe>caffe>coffe>coffee
先创建一个6×8的表(cafe长度为4,coffee长度为6,各加2)(1):

  

  
c o f f e e

  

  

  

  

  

  

  

  
c
  

  

  

  

  

  

  
a
  

  

  

  

  

  

  
f
  

  

  

  

  

  

  
e
  

  

  

  

  
1
接着,在如下位置填入数字(表2):

  

  
c o f f e e

  
0 1 2 3 4 5 6
c 1
  

  

  

  

  

  
a 2
  

  

  

  

  

  
f 3
  

  

  

  

  

  
e 4
  

  

  

  
2
从3,3格开始,开始计算。取以下三个值的最小值:
  • 如果最上方的字符等于最左方的字符,则为左上方的数字。否则为左上方的数字+1。(对于3,3来说为0)
  • 左方数字+1(对于3,3格来说为2)
  • 上方数字+1(对于3,3格来说为2)
因此为格3,3为0(表3)

  

  
c o f f e e

  
0 1 2 3 4 5 6
c 1
  0 

  

  

  

  

  
a 2
  

  

  

  

  

  
f 3
  

  

  

  

  

  
e 4
  

  

  

  
3
  
  
同理,推出下表

  

  
c o f f e e

  
0 1 2 3 4 5 6
c 1 0 1 2 3
  
4
  
5
  
a 2 1 1 2 3 4 5
f 3 2 2 1 2 3 4
e 4 3 3 2 2 2 3
取右下角,得编辑距离为3

C/C++伪代码

动态规划经常被用来作为这个问题的解决手段之一。
整数 Levenshtein距离(字符串 str1[1..m], 字符串 str2[1..n])
//声明变量, d[i , j]用于记录str1[1...i]与str2[1..j]的Levenshtein距离
int d[0..m, 0..n]
//初始化
for i from 0 to m
d[i, 0] := i
for j from 0 to n
d[0, j] := j
//用动态规划方法计算Levenshtein距离
for i from 1 to m
for j from 1 to n
{
//计算替换操作的代价,如果两个字符相同,则替换操作代价为0,否则为1
if str1[i]== str2[j] then cost := 0
else cost := 1
//d[i,j]的Levenshtein距离,可以有
d[i, j] := minimum(
d[i-1, j] + 1, //在str1上i位置删除字符(或者在str2上j-1位置插入字符)
d[i, j-1] + 1, //在str1上i-1位置插入字符(或者在str2上j位置删除字符)
d[i-1, j-1] + cost // 替换操作
)
}
//返回d[m, n]
return d[m, n]
wikisource上有不同的编程语言的版本。

代码

pascal代码

procedurelevenshtein;
var
st1,st2:string;
d:array[0..1000000] of integer;
i,j,m,n,cost:integer;
begin
m:=length(st1);
n:=length(st2);
for i:=0 to m do
d[i,0]:=i;
for j:= 0 to n do
d[0,j]:=j;
for i:= 1 to m do
for j:= 1 to n do
begin
if st1[i]=st2[j] then
cost:=0
else cost:=1;
if cost=0 then
begin
if (d[i-1,j]<d[i,j-1]) and (d[i-1,j]+1<d[i-1,j-1]+cost)
then d[i,j]:=d[i-1,j]+1
else if d[i,j-1]+1< d[i-1,j-1]+cost
then d[i,j]:=d[i,j-1]+1
else d[i,j]:=d[i-1,j-1]+cost;
end;
end;

Java代码

publicclassStringSimilar{


 //编辑距离求串相似度
 

publicdoublegetStringSimilar(Strings1,Strings2){

 
//TODOAuto-generatedmethodstub

 
doubled[][];//matrix

 intn;//lengthofs

 intm;//lengthoft

 inti;//iteratesthroughs

 intj;//iteratesthrought

 chars_i;//ithcharacterofs

 chart_j;//jthcharacteroft

 doublecost;//cost 
//Step1
 n=s1.length();
 m=s2.length();
 if(n==0){
 returnm;
 }
 if(m==0){
 returnn;
 }
 d=newdouble[n+1][m+1];
 //Step2
 for(i=0;i<=n;i++){
 d[i][0]=i;
 }
 for(j=0;j<=m;j++){
 d[0][j]=j;
 }
 //Step3
 for(i=1;i<=n;i++){
 s_i=s1.charAt(i-1);
 //Step4
 for(j=1;j<=m;j++){
 t_j=s2.charAt(j-1);
 //Step5
 if(s_i==t_j){
 cost=0;
 }else{
 cost=1;
 }
 //Step6
 d[i][j]=Minimum(d[i-1][j]+1,d[i][j-1]+1, d[i-1][j-1]+cost);
 }
 }
 //Step7
 returnd[n][m];
 }
 //求最小值
 privatedoubleMinimum(doublea,doubleb,doublec){
 //TODOAuto-generatedmethodstub
 doublemi;
 mi=a;
 if(b<mi){
 mi=b;
 }
 if(c<mi){
 mi=c;
 }
 returnmi;
 }
 } 

VB代码

FunctionEditDistance(Str1AsString,Str2AsString)AsLong
DimDistances()AsLong'用来储存数据
DimLen1AsLong,Len2AsLong'存储字符串长度
Len1=Len(Str1):Len2=Len(Str2)
IfLen1=0Then'当第一个字符串长度为0
EditDistance=Len2'返回Len2
ElseIfLen2=0Thne'第二个字符串长度为0
EditDistance=Len1'返回Len1
Else'长度均不为0
RedimDistances(0ToLen1,OToLen2)'二维数组重定义
DimiAsLong,jAsLong,Nums(1To3)AsLong'Nums用来存储三个值
DimMinNumAsLong'用来存储Nums中最大值
'初始化数据
Fori=0ToLen1
Distance(i,0)=i
Nexti
Fori=0ToLen2
Distance(0,i)=i
Nexti
'正式计算
Fori=1ToLen1
Forj=1ToLen2
Nums(1)=Distances(i-1,j)+1
Nums(2)=Distances(i,j-1)+1
IfMid(Str1,i,1)=Mid(Str2,j,1)Then'如果Str1第i个字符和Str2第j个字符相等
Nums(3)=Distance(i-1,j-1)
Else'如果不相等
Nums(3)=Distance(i-1,j-1)+1
EndIf
MinNum=Nums(1)
IfMinNum>Nums(2)ThenMinNum=Nums(2)
IfMinNum>Nums(3)ThenMinNum=Nums(3)
Distances(i,j)=MinNum
Nextj
Nexti
'返回右下角值
EditDistance=Distances(Len1,Len2)
EndIf
EndFunction

$firstVoiceSent
- 来自原声例句
小调查
请问您想要如何调整此模块?

感谢您的反馈,我们会尽快进行适当修改!
进来说说原因吧 确定
小调查
请问您想要如何调整此模块?

感谢您的反馈,我们会尽快进行适当修改!
进来说说原因吧 确定