莱文斯坦距离的过程

1,017 阅读5分钟

前言

本小白经过一晚上的研究,对莱文斯坦距离有了一定的了解,莱文斯坦距离有三个步骤,我将每一个步骤研究
透彻就知道这玩意到底讲的是啥了

定义

莱文斯坦距离,又称Levenshtein距离,是编辑距离的一种。指两个字串之间,由一个转成另一个所需的最少
编辑操作次数。允许的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。

当我们知道什么时候替换、什么时候插入、什么时候删除的时候这道题是不是就迎刃而解了呢

1.分析

1.1 添加

注明: 以下都以a为基准数组

a = "ab**"
b = "b***"
这时候我们做添加操作
我们能发现规律:
i=0,j=0
当 a[i]!==b[j]&&a[i+1]==b[j]的时候我们进行添加操作,操作数加1

1.2 删除

a="a***"
b="ba**"
我们进行删除
我们能发现规律 :
当 a[i]!==b[j]&&a[i]==b[j+1]的时候我们执行删除操作,操作数加1

1.3 替换

a="ab**"
b="cb**"
或者a="ab***"
    b="cd***"
或者a="***ab*"
    b="***c"
或者a="***a"
    b="***bc***"
我们进行替换
我们能发现规律: 
当 a[i]!=b[j]&&a[i+1]==b[j+1]或者
a[i]!=b[j]&&a[i+1]!=b[j]&&a[i+1]!=b[j+1]&&a[i]!=b[j+1]或者
a[i+1]=undefined或者b[j+1]=undefined的时候我们执行替换操作,操作数加1

1.4 但是我们会发现添加删除和替换会有重合的地方

a="aa***"
b="ba***"
如果直接替换只需要一次操作
如果删除的话还需要添加,两次操作
所以当碰到满足替换条件的时候直接执行替换操作

所以删除和增加的条件上要加上替换条件不成立的时候的条件即
删除:a[i]!==b[j]&&a[i+1]==b[j]&&a[i+1]!=b[j+1]
增加:a[i]!==b[j]&&a[i]==b[j+1]&&a[i+1]!=b[j+1]

1.5 为啥我们只比较位置距离只相差1的数字呢

a="a**"
b="**a"
因为这样只用替换一次操作就可以搞定
如果a[i]!==b[i]&&a[i]!==b[i+1]&&a[i]==b[i+2]
我们一次替换操作就可以解决的问题难到需要两次删除吗
所以当距离超过1,有元素相等的时候我们直接不考虑

1.6 当不是同时结束的时候

b先结束的时候
a="abcd****"
b="abcd"
显而易见b直接添加后面的数,操作数加上a.length-b.length

a先结束的时候
a="abcd"
b="abcd****"
显而易见b直接删除后面的数,操作数加上b.length-a.length

2.实例

a="mitcmu"
b="mtacnu"

1.第一步

i=0,j=0 c=0
a[i]=m,b[j]=m
a[i]==b[j]
不做操作比较下一个数 a中的'i', 和b中的't'
i=i+1=2,j+j+1=2,c=0

2.第二步

a[i]=i,a[i+1]=t,b[j]=t,b[j+1]=a
满足a[i]!=b[j]&&a[i+1]==b[j]&&a[i+1]!=b[j+1]增加条件进行增加操作
理想:a="mitcmu"
      b="mitacnu"
现实:a="mitcmu"
      b="mtacnu"
理想中b添加字符i,因为程序的设计只是计算操作数,所以实际上并没有添加,只是记录操作数 
c=c+1=1
进行比较下一个数a中的't'和b中的't'
i=i+1=3,j=j=2

3.第三步

因为a[i]=b[j]
不进行操作比较下一个数 a中的c, b中的a
i=i+1=4,j=j+1=3

4.第四步

a[i]=c,a[i+1]=m,b[j]=a,b[j+1]=c
满足a[i]!==b[i]&&a[i]==b[i+1]&&[i+1]!=b[i+1]删除条件我们执行删除操作
理想:a="mitcmu"
      b="mitcnu"
现实:a="mitcmu"
      b="mtacnu"
记录步数c=c+1=2
因为将数进行删除操作所以 a中的'c'重新和b中的'c'进行比较
i=i=4,j=j+1=4

5.第五步

a[i]=c,b[j]=c
a[i]=b[j]不进行任何操作比较下一个数a中的'm'和b中的'n'
i=i+1=5,j=j+1=5

6.第六步

a[i]=m,b[j]=n,a[i+1]=u,b[j+1]=u
满足a[i]!==b[i]&&a[i+1]==b[i+1]条件执行替换操作
理想:a="mitcmu"
      b="mitcmu"
现实:a="mitcmu"
      b="mtacnu"
记录步数c=c+1=3
进行下个数的比较 a中的'u'和b中的'u'
i=i+1=6,j=j+1=6

7.第七步

a[i]=u,b[j]=u
a[i]=b[j]
不进行任何操作进行下一个数的比较
没有了结束   返回c=3

实现代码

var a = "mitcmu";
var b = "mtacnu";
for(let i = 0, j=0 ,edist=0 ;i <= a.length || j <= b.length;){
    if (i == a.length || j == b.length) { 
    // 不一定同时到达
        if (i < a.length) edist += (a.length - i); 
        if (j < b.length) edist += (b.length - j);
        console.log(edist)
        return;
    }

    if (a[i] == b[j]) {
        // 不做操作
        i = i + 1;
        j = j + 1;
    }
    else if(a[i+1]=b[j+1])
    {
        //替换
        i = i + 1;
        j = j + 1;
        edist += 1;
    }
    else if (a[i + 1] == b[j]) {
        //增加
        i = i + 1;
        edist += 1;
    }
    else if (a[i] == b[j + 1]) {
        //删除
        j += 1
        edist += 1;
    }else{
        //替换
        i = i + 1;
        j = j + 1;
        edist += 1;  
    }
}

总结

任何递归都可以用循环来实现,我们只要掌握实现它的原理便可以简单的将它写出来,但是当使用递归时,如
果处理的操作过多就会栈溢出得不到我们想要的结果,会执行不了。然而循环就能很好的实现我们想要的结果。
如果问题请及时反馈,本文只是个人见解。