编辑距离算法(LD)的理解

结合网上各位大佬的文章以及自己的理解,写给自己看。具体的理论我反正是看不懂😂。

A字符串转换为B字符串的最小操作次数(操作字符包括:新增一个字符,删除一个字符,修改一个字符),被称为编辑距离,LD只是其中的一种算法(俄国科学家Levenshtein提出,又叫Levenshtein Distance)。算好编辑距离就对应的可以算相似度(编辑距离+1的倒数,如:abb变为a需要删除两个b,距离是2相似度就是是\frac{1}{2+1})。

计算距离先建立个二维数组。

就拿大家都用的例子

        A字符串为GGATCGA。

        B字符串为GAATTCAGTTA。

这个我认为最简单的就是删除A字符串第一个字符串后的所有,然后加上B字符串第一个字符串后面的所有(😂,没办法我第一眼觉得最简单的方法就是这样。),但是不是最小编辑次数。实际上的应该是如下

GGA_TC_G__A
GAATTCAGTTA
黄色的表示修改,绿色的表示新增,A到B需要修改一个G为A,新增一个T,然后新增一个A,再新增两个T,编辑距离是5。相似度是\frac{1}{5+1}=0.16。

下面是说明

一、先建立一个矩阵,二维数组长度分别为A字符串长度和B字符串长度+1

GAATTCAGTTA
01234567891011
G1
G2
A3
T4
C5
G6
A7

表一

二、计算剩下单元格的值

       根据说明LD有如下公式;Len(A)表示A字符串的长度,Rev(A)表示A字符串的反转

  1. LD(A,A)=0
  2. LD(A,'')=Len(A)
  3. LD(A,B)=LD(B,A)
  4. LD(A,B)=LD(Rev(A),Rev(B))
  5. 0≤LD(A,B)≤Max(Len(A),Len(B))
  6. LD(A+C,B+C)=LD(A,B)
  7. LD(A+B,A+C)=LD(B,C)
  8. LD(A,B)≤LD(A,C)+LD(B,C)【LD(A,B)最小值是A==B,结果为0。最大是A或者B其中一个为空字符串,结果是MAX(Len(A),Len(B)。最小值很容易就是A==B,然后假设C==B,或者C!=B即可得出。最大值假设A为空字符串,然后假设B==C和假设B与C完全不同的情况下分别带入即可。也可用大家文章中都用的记法,“三角形,两边之和大于第三边”,或者干脆死记】
  9. LD(A+C,B)≤LD(A,B)+LD(B,C)

A字符串由1到n个字符组成所以有A=a1a2…an组成。

B字符串由1到m个字符组成所以有B=b1b2…bm组成。

Len(A)=N,Len(B)=M,设LD(i,j)=LD(a1a2…an,b1b2…bm)。

这里的a1是a_{1},b1同理,因为第一次用这个写文章不知道怎么把下标打出来,后面才知道用MD编辑器就行了😂

得LD(N,M)=LD(A,B),LD(0,0)=0,LD(0,j)=j,LD(i,0)=i,所以表一的第一行和第一列就可以根据LD(0,j)和LD(i,0)算出。

a~i~表示A字符串的第i个字符。

当a~i~==b~j~的情况下,有公式LD(i,j)=LD(i-1,j-1),取当前单元格矩阵左上角的值。

否则的话取LD(i,j)=Min(LD(i-1,j-1),LD(i-1,j),LD(i,j-1))+1,也就是取当前单元格左上角,上边,左边中最小的那个值加1。

另外一种计算就是当前单元格的左上角,上边,左边都加1,取最小值,如果a~i~==b~j~,左上角的值不加1改为加0。

根据上面说的规则填写矩阵中的值。

GAATTCAGTTA
01234567891011
G1012345678910
G211234566789
A321123456788
T432212345678
C543322234567
G654433333456
A765444434455

矩阵中最后一个值就是编辑距离,所以LD('GGATCGA','GAATTCAGTTA')=LD(7,11)=5

算矩阵值代码


function calculation_matrix(A, B)
{
  var m=A.length;
  var n=B.length;
  var matrix = [];
  var i;
  var j;
  for (i=0;i<=m;i++)
  {
    matrix.push([]);
    matrix[i] = [i];
  }
  for (i=0;i<=n;i++)
  {
    matrix[0].push(i);
  }
 
  for (i=1;i<m + 1;i++)
  {
    for (j=1;j<n + 1;j++)
    {
      var c_a_c=  A[i -1];
      var c_b_c=  B[j -1];
      if (c_a_c == c_b_c)
      {
        matrix[i][j] = matrix[i-1][j-1];
      } 
      else 
      {
        matrix[i][j] = Math.min(matrix[i-1][j], matrix[i][j-1], matrix[i-1][j-1]) + 1;
      }
    }
  }
  return matrix;
}

三、对比字符串差异

定义空字符串C和D

以矩阵右下角为起点,且有如下规则。

字符a~i~==b~j~的时候,就标记当前单元格左上角,否则取左上角,上边,右边三个单元格中值最小的一个单元格标记。如果其中有相同的值,就按左上角,上边,右边的顺序取最小值。

当单元格处于第一行的时候,下一个单元格只能是左边,当单元格在第一列的时候,下个单元格只能是上边。

由以上规则计算标记如下

GAATTCAGTTA
01234567891011
G1012345678910
G211234566789
A321123456788
T432212345678
C543322234567
G654433333456
A765444434455

i=7,j=11,matrix表示矩阵。

A[i-1]=B[j-1],定位matrix[6][10],i--,j--后i=6,j=10;

A[5]!=B[9],取matrix[6][10],左上角,左边,上边最小值的单元格matrix[6][9],i不变,j--;

A[5]!=B[8],取matrix[6][9],左上角,左边,上边最小值的单元格matrix[6][8],i不变,j--;

A[5]==B[7],取matrix[6][8]的左上角单元格matrix[5][7],i--,j--;

...

直到定位左上角。

标记左上角的表示相同或者是修改。将a~i~添加到C,将b~j~添加到D。

标记上边的表示删除,将a~i~添加到C,将'_'添加到D。

标记左边的表示新增,将'_'添加到C,将b~j~添加到D。

对比字符串代码部分


function backtracking(matrix,  A, B) {
    var i=A.length;
    var j=B.length;
    var fix_a='';
    var fix_b='';
    while (i>0 && j>0) 
    {
        var c_a_c=  A[i -1];
        var c_b_c=  B[j -1];
        if (c_a_c == c_b_c)
        {
            fix_a = c_a_c+fix_a;
            fix_b = c_b_c+fix_b;
            --i;
            --j;
        } else{
            
            var w = 0;
            var l_t=matrix[i-1][j-1];
            var t_=matrix[i-1][j];
            var l_=matrix[i][j-1];
            var min_id=Math.min(l_t,t_,l_);
            if (min_id==l_t)
            {
                w=0;
                i--;
                j--;
            }else if (min_id==t_)  {
                i--;
                w=1;
            } else {
                j--;
                w=-1;
            } 
            if (w==0)  {
                //修改
                fix_a = "<edit>"+c_a_c+"</edit>"+fix_a;
                fix_b = "<edit>"+c_b_c+"</edit>"+fix_b;
            } else if (w == -1) {
                //回溯到左边,说明新增
                fix_b = '<ins>'+c_b_c+'</ins>'+fix_b;
            } else if (w == 1)  {
                //回溯到上边 说明删除
                fix_a = '<del>'+c_a_c+'</del>'+fix_a;
            }
        }   
    }
    if(j!=0){
        //说明字符串只能修改a所有字符串,然后新增对应长度的b字符串
        fix_b = '<ins>'+B.substr(0,j)+'</ins>'+fix_b;
    }else if(i!=0){
        //说明字符串只能删除a部分字符串
        fix_a = '<del>'+A.substr(0,i)+'</del>'+fix_a;
        console.log([fix_a,fix_b]);
    }
    console.log([fix_a,fix_b]);
   
    return [fix_a,fix_b]
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值