最短编辑距离

来源:互联网 发布:51单片机视频教程全集 编辑:程序博客网 时间:2024/05/22 01:43

1.最短编辑距离的介绍

①基本定义

      所谓编辑距离(Edit Distance),是指两个字符串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作总共有三个:将一个字符替换成另一个字符、插入一个字符或者删除一个字符。讲道理的话,编辑距离越小,两个字符串就越相似。对了,它又叫作Levenshtein距离,因为这个概念是在1965年由俄罗斯科学家Vladimir Levenshtein提出的。

②举例说明

    最短编辑距离这个编程概念有些复杂,接下来我将为大家举几个例说明:

I.将一个字符替换成另一个字符

例如:

tall--->ball

she--->shy

luck--->lock

II.插入一个字符

比如:

fed--->feed

to--->two

at--->ant

III.删除一个字符

譬如:

open--->pen

changed--->change

down--->own

IV.综合

综上所述,可以转化两个差别较大的字符串,就像这样:

java--->pava--->pasa--->pasca--->pascal

如果现在大家还有什么不懂的,请在评论处说明,我一定第一时间解答。

2.最短编辑距离的公式

①公式总结

总的说来,有四种情况:

I.删除

a[i-1][j]+1;

II.插入

a[i][j-1]+1;

III.替换

a[i][j]=a[i-1][j-1]+1;

IV.不变(相等时)

a[i][j]=a[i-1][j-1];

②代码实现

声明:本人只会C/C++,其它的是从网上搜集的。

I*C++(稍微改改就成了C语言)

#include<cstdio>#include<cstring>#include<iostream>using namespace std;char a[1001],b[1001];int lena,lenb,f[1001][1001];int min(int x,int y,int z){if(x<=y&&x<=z) return x;else if(y<=x&&y<=z) return y;else return z;}main(){scanf("%s%s",a,b);lena=strlen(a);lenb=strlen(b);for(int i=0;i<=lena;i++)for(int j=0;j<=lenb;j++){if(!i)  {  f[i][j]=j;  continue;  }  if(!j)  {  f[i][j]=i;  continue;  }  if(a[i-1]==b[j-1])f[i][j]=f[i-1][j-1];elsef[i][j]=1+min(f[i-1][j-1],f[i][j-1],f[i-1][j]);}cout<<f[lena][lenb]<<endl;}

II*pascal

varst1,st2:string;d:array[0..1000000] of integer;i,j,m,n,cost:integer;beginm:=length(st1);n:=length(st2);for i:=0 to m dod[i,0]:=i;for j:= 0 to n dod[0,j]:=j;for i:= 1 to m dofor j:= 1 to n dobeginif st1[i]=st2[j] thencost:=0else cost:=1;if cost=0 thenbeginif (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]+1else if d[i,j-1]+1< d[i-1,j-1]+costthen d[i,j]:=d[i,j-1]+1else d[i,j]:=d[i-1,j-1]+cost;end;end;

III*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; } } 

IV*R语言

leven <- function(stra,strb){    lena=nchar(stra)    lenb=nchar(strb)    tb=array(0,c(lena+1,lenb+1))    tb[1,]=0:lenb    tb[,1]=0:lena    for(i in 1:lena)    {        for(j in 1:lenb)        {            row=i+1            col=j+1            k=1            if(substr(stra,i,i)==substr(strb,j,j))k=0            step=min(tb[i,j]+k,tb[i+1,j]+1,tb[i,j+1]+1)            tb[i+1,j+1]=step        }    }    return(tb[lena+1,lenb+1])}

V*Scala

import Math.min def distance(s0: String, s1: String): Int = {  if (s0.isEmpty) return s1.length  if (s1.isEmpty) return s0.length  val dis = Array.ofDim[Int](s0.length, s1.length)  for (i <- 0 until s0.length; j <- 0 until s1.length)    dis(i)(j) = (i, j) match {      case (0, j) => j      case (i, 0) => i      case _ =>        if (s0(i) == s1(j))          dis(i - 1)(j - 1)        else          min(min(dis(i - 1)(j) + 1, dis(i)(j - 1) + 1), dis(i - 1)(j - 1) + 1)    }   return dis(s0.length - 1)(s1.length - 1)}

VI*VB

Function EditDistance(Str1 As String,Str2 As String)As Long     Dim Distances() As Long '用来储存数据 Dim Len1 As Long, Len2 As Long '存储字符串长度     Len1 = Len(Str1): Len2 = Len(Str2) If Len1 = 0 Then'当第一个字符串长度为0         EditDistance=Len2'返回Len2 ElseIf Len2 = 0 Then'第二个字符串长度为0         EditDistance = Len1'返回Len1 Else'长度均不为0         Redim Distances(0ToLen1, OToLen2)'二维数组重定义 Dim i As Long, j As Long, Nums(1 To 3) As Long'Nums用来存储三个值         Dim MinNum As Long'用来存储Nums中最大值 '初始化数据         For i = 0 To Len1 Distance(i, 0) = i         Next i  For i = 0 To Len2              Distance(0, i)=i Next i '正式计算         For i = 1 To Len1 For j = 1 To Len2                  Nums(1) = Distances(i - 1, j) + 1 Nums(2) = Distances(i, j - 1) + 1                  If Mid(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                   End If MinNum = Nums(1)                  If MinNum > Nums(2) Then MinNum = Nums(2) If MinNum > Nums(3) Then MinNum = Nums(3)                  Distances(i, j) = MinNum Next j         Nexti '返回右下角值 EditDistance = Distances(Len1, Len2)     End If  End Function

VII*C#

public class StringComparator    {        public static int LevenshteinDistance(string source, string target)        {            int columnSize = source.Length;            int rowSize = target.Length;            if (columnSize == 0)            {                return rowSize;            }            if (rowSize == 0)            {                return columnSize;            }            int[,] matrix = new int[rowSize + 1, columnSize + 1];            for (int i = 0; i <= columnSize; i++)            {                matrix[0, i] = i;            }            for (int j = 1; j <= rowSize; j++)            {                matrix[j, 0] = j;            }            for (int i = 0; i < rowSize; i++)            {                for (int j = 0; j < columnSize; j++)                {                    int sign;                    if (source[j].Equals(target[i]))                        sign= 0;                    else                        sign = 1;                    matrix[i + 1, j + 1] = Math.Min(Math.Min(matrix[i, j] +  sign, matrix[i + 1, j]), matrix[i, j + 1] + 1);                }            }            return matrix[rowSize, columnSize];        }        public static float StringSimilarity(string source, string target)        {            int distance = LevenshteinDistance(source, target);            float maxLength = Math.Max(source.Length, target.Length);            return (maxLength - distance) / maxLength;        }    }

VIII*SQL

CREATE FUNCTION edit_distance(@s1 nvarchar(3999), @s2 nvarchar(3999))RETURNS intASBEGINDECLARE @s1_len int, @s2_len intDECLARE @i int, @j int, @s1_char nchar, @c int, @c_temp intDECLARE @cv0 varbinary(8000), @cv1 varbinary(8000)SELECT@s1_len = LEN(@s1), @s2_len = LEN(@s2), @cv1 = 0x0000,@j = 1, @i = 1, @c = 0WHILE @j <= @s2_lenSELECT @cv1 = @cv1 + CAST(@j AS binary(2)), @j = @j + 1WHILE @i <= @s1_lenBEGINSELECT@s1_char = SUBSTRING(@s1, @i, 1), @c = @i,@cv0 = CAST(@i AS binary(2)), @j = 1WHILE @j <= @s2_lenBEGINSET @c = @c + 1SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j-1, 2) AS int) +CASE WHEN @s1_char = SUBSTRING(@s2, @j, 1) THEN 0 ELSE 1 ENDIF @c > @c_temp SET @c = @c_tempSET @c_temp = CAST(SUBSTRING(@cv1, @j+@j+1, 2) AS int)+1IF @c > @c_temp SET @c = @c_tempSELECT @cv0 = @cv0 + CAST(@c AS binary(2)), @j = @j + 1ENDSELECT @cv1 = @cv0, @i = @i + 1ENDRETURN @cEND

3.最短编辑距离的应用

①DNA分析

基因学的一个主要主题就是比较 DNA 序列并尝试找出两个序列的公共部分。如果两个 DNA 序列有类似的公共子序列,那么这些两个序列很可能是同源的。在比对两个序列时,不仅要考虑完全匹配的字符,还要考虑一个序列中的空格或间隙(或者,相反地,要考虑另一个序列中的插入部分)和不匹配,这两个方面都可能意味着突变(mutation)。在序列比对中,需要找到最优的比对(最优比对大致是指要将匹配的数量最大化,将空格和不匹配的数量最小化)。如果要更正式些,可以确定一个分数,为匹配的字符添加分数、为空格和不匹配的字符减去分数。
全局序列比对尝试找到两个完整的序列 S1和 S2之间的最佳比对。以下面两个 DNA 序列为例:
S1= GCCCTAGCG
S2= GCGCAATG
如果为每个匹配字符一分,一个空格扣两分,一个不匹配字符扣一分,那么下面的比对就是全局最优比对:
S1'= GCCCTAGCG
S2'= GCGC-AATG
连字符(-)代表空格。在 S2'中有五个匹配字符,一个空格(或者反过来说,在 S1'中有一个插入项),有三个不匹配字符。这样得到的分数是 (5 * 1) + (1 * -2) + (3 * -1) = 0,这是能够实现的最佳结果。
使用局部序列比对,不必对两个完整的序列进行比对,可以在每个序列中使用某些部分来获得最大得分。使用同样的序列 S1和 S2,以及同样的得分方案,可以得到以下局部最优比对 S1''和 S2'':
S1 = GCCCTAGCG
S1''= GCG
S2''= GCG
S2 = GCGCAATG
这个局部比对的得分是 (3 * 1) + (0 * -2) + (0 * -1) = 3。

②拼写纠错(Spell Correction)

又称拼写检查(Spell Checker),将每个词与词典中的词条比较,英文单词往往需要做词干提取等规范化处理,如果一个词在词典中不存在,就被认为是一个错误,然后试图提示N个最可能要输入的词——拼写建议。常用的提示单词的算法就是列出词典中与原词具有最小编辑距离的词条。

这里肯定有人有疑问:对每个不在词典中的词(假如长度为len)都与词典中的词条计算最小编辑距离,时间复杂度是不是太高了?的确,所以一般需要加一些剪枝策略,如:

    因为一般拼写检查应用只需要给出Top-N的纠正建议即可(N一般取10),那么我们可以从词典中按照长度依次为len、len-1、len+1、len-2、len-3、...的词条比较;
    限定拼写建议词条与当前词条的最小编辑距离不能大于某个阈值;
    如果最小编辑距离为1的候选词条超过N后,终止处理;
    缓存常见的拼写错误和建议,提高性能。

③命名实体抽取(Named Entity Extraction)

由于实体的命名往往没有规律,如品牌名,且可能存在多种变形、拼写形式,如“IBM”和“IBM Inc.”,这样导致基于词典完全匹配的命名实体识别方法召回率较低,为此,我们可以使用编辑距离由完全匹配泛化到模糊匹配,先抽取实体名候选词。

具体的,可以将候选文本串与词典中的每个实体名进行编辑距离计算,当发现文本中的某一字符串的编辑距离值小于给定阈值时,将其作为实体名候选词;获取实体名候选词后,根据所处上下文使用启发式规则或者分类的方法判定候选词是否的确为实体名。

④实体共指(Entity Coreference)

通过计算任意两个实体名之间的最小编辑距离判定是否存在共指关系?如“IBM”和“IBM Inc.”, "Stanford President John Hennessy "和"Stanford University President John Hennessy"。

⑤机器翻译(Machine Translation)

识别平行网页对:由于平行网页通常有相同或类似的界面结构,因此平行网页在HTML结构上应该有很大近似度。首先将网页的HTML标签抽取出来,连接成一个字符串,然后用最小编辑距离考察两个字符串的近似度。实际中,此策略一般与文档长度比例、句对齐翻译模型等方法结合使用,以识别最终的平行网页对。
    自动评测:首先存储机器翻译原文和不同质量级别的多个参考译文,评测时把自动翻译的译文对应到与其编辑距离最小的参考译文上,间接估算自动译文的质量。

⑥字符串核函数(String Kernel)

最小编辑距离作为字符串之间的相似度计算函数,用作核函数,集成在SVM中使用。