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

注:这篇博客讨论的算法是怎样求解两个字符串的最小编辑距离,其目的是为了下一篇的虚拟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 
#include 
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<strlen(s1), strlen(s2))<return 0;
}

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

利用动态规划:

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

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)

你可能感兴趣的:(算法)