最小编辑距离问题(Edition Distance)

来源:互联网 发布:杭州少儿趣味编程 编辑:程序博客网 时间:2024/05/17 22:25

注:这篇博客讨论的算法是怎样求解两个字符串的最小编辑距离,其目的是为了下一篇的虚拟DOM,来做一个预备工作,这里主要讨论的用LevenshteinDistance,主要通过的是动态规划。

什么是最小编辑距离:

给定一个长度为m和n的两个字符串,设有以下几种操作:替换(R),插入(I)和删除(D)且都是相同的操作。寻找到转换一个字符串插入到另一个需要修改的最小(操作)数量。这个数量就可以被视为最小编辑距离。如:acd与ace的EditionDistance距离为1,abc与cab的距离为1。

递归实现:

我们来讨论一下情况:
A[i]B[i]相等时,我们只需要比较其A[i+1...len]B[i+1...len]
A[i]B[i]不等时,我们可以做如下操作:
1. 在A[i+1]增加一个字符,从而比较A[i...0]B[i1...0]
2. 在B[i+1]增加一个字符,从而比较B[i...0]A[i1...0]
3. 更改A[i]或者B[i]处字符,从而比较A[i1...0]B[i1...0]
4. 删除A[i]处字符,从而比较A[i1...0]B[i...0]
5. 删除B[i]处字符,从而比较B[i1...0]A[i...0]
情况搞清楚了,代码我们非常快的就能写出:

#include <iostream>#include <cmath>using namespace std;inline int getMin (int a, int b, int c) {    return min(min(a, b), c);}int calcDistance(char* A, char* B, int m, int n) {    if (m == 0 && n == 0)         return 0;    if (m == 0)         return n;    if (n == 0)        return m;    int caseA = calcDistance(A, B, m - 1, n) + 1;      int caseB = calcDistance(A, B, m, n - 1) + 1;    int caseC = calcDistance(A, B, m - 1, n - 1) + (A[m] != B[n]);    return getMin(caseA, caseB, caseC);}int main () {    char s1[20], s2[20];    cin>>s1>>s2;    cout<<calcDistance(s1, s2, strlen(s1), strlen(s2))<<endl;    return 0;}

但是这种方法的缺点在哪里呢,就像求解斐波那契数列那样,递归造成了重复计算(如:我们可以在不同的递归函数中调用已经调用过的函数),我们再来看看最小编辑距离问题具有重叠子问题,最优子结构,所以我们可以用动态规划来进行求解,这样就避免了重复计算的问题。

利用动态规划:

动态规划的方式,我们只是对递归进行一下变形,也就是对我们已经求得的值进行保存,首先我们要初始化一张表出来,也就是一个二维数组,N[A.length + 1][B.length + 1]
形成表如图:(NA的长度,MB的长度)

0N...00M00

我们来总结一下我们上面所述几种情况的表现,可以分为4类:
1. A或者B字符相等,N[i,j]等同于N[i,j]
2. 更改A或者B字符,N[i,j]等同于N[i1,j1]
3. 删除A字符或者增加B字符,N[i,j]等同于N[i1][j]+1
4. 删除B字符或者增加A字符,N[i,j]等同于N[i][j1]+1
最终N[i,j]的值就为min(情况1 || 情况2, 情况3, 情况4)的最小值,并且N[i,j]代表的是A[0i]B[0j]的最小编辑距离。
代码如下:

int calcDistanceDP (char* A, char* B) {    int m = strlen(A), n = strlen(B);    // 生成表    int *T = (int *)malloc(m * n * sizeof(int));    // 赋初始值    for (int i = 0; i <= m; i++)         for (int j = 0; j <= n; j++)            *(T + i * n + j) = 0;    for (int i = 0; i <= m; i++)        *(T + i * n) = i;    for (int i = 0; i <= n; i++)        *(T + i) = i;    for (int i = 0; i < m; i++)     {        for (int j = 0; j < n; j++)         {            int cost = (int)A[i] != B[j];            int caseA = *(T + i * n + j + 1) + 1;            int caseB = *(T + (i + 1) * n + j) + 1;            int caseC = *(T + i * n + j) + cost;            *(T + (i + 1) * n + j + 1) = getMin(caseA, caseB, caseC);        }    }    return *(T + m * n - 1);}

算法复杂度为O(MN)

0 0
原创粉丝点击