算法之美--3.3.1 全局编辑距离

编辑距离概念描述:

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

例如将kitten一字转成sitting:

  1. sitten (k→s)
  2. sittin (e→i)
  3. sitting (→g)

俄罗斯科学家Vladimir Levenshtein在1965年提出这个概念。

问题:找出字符串的编辑距离,即把一个字符串s1最少经过多少步操作变成编程字符串s2,操作有三种,添加一个字符,删除一个字符,修改一个字符

解析:

该算法和书本上的德勒曼-温施算法思路一样。

首先定义这样一个函数——edit(i, j),它表示第一个字符串的长度为i的子串到第二个字符串的长度为j的子串的编辑距离。

显然可以有如下动态规划公式:

  • if i == 0 且 j == 0,edit(i, j) = 0
  • if i == 0 且 j > 0,edit(i, j) = j
  • if i > 0 且j == 0,edit(i, j) = i
  • if i ≥ 1  且 j ≥ 1 ,edit(i, j) == min{ edit(i-1, j) + 1, edit(i, j-1) + 1, edit(i-1, j-1) + f(i, j) },当第一个字符串的第i个字符不等于第二个字符串的第j个字符时,f(i, j) = 1;否则,f(i, j) = 0。
  0 f a i l i n g
0                
s                
a                
i                
l                
n                
  0 f a i l i n g
0 0 1 2 3 4 5 6 7
s 1              
a 2              
i 3              
l 4              
n 5              

 计算edit(1, 1),edit(0, 1) + 1 == 2,edit(1, 0) + 1 == 2,edit(0, 0) + f(1, 1) == 0 + 1 == 1,min(edit(0, 1),edit(1, 0),edit(0, 0) + f(1, 1))==1,因此edit(1, 1) == 1。 依次类推:

  0 f a i l i n g
0 0 1 2 3 4 5 6 7
s 1 1 2 3 4 5 6 7
a 2 2            
i 3              
l 4              
n 5              

edit(2, 1) + 1 == 3,edit(1, 2) + 1 == 3,edit(1, 1) + f(2, 2) == 1 + 0 == 1,其中s1[2] == 'a' 而 s2[1] == 'f'‘,两者不相同,所以交换相邻字符的操作不计入比较最小数中计算。以此计算,得出最后矩阵为:

  0 f a i l i n g
0 0 1 2 3 4 5 6 7
s 1 1 2 3 4 5 6 7
a 2 2 1 2 3 4 5 6
i 3 3 2 1 2 3 4 5
l 4 4 3 2 1 2 3 4
n 5 5 4 3 2 2 2 3

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 int min(int a, int b)
 7 {
 8     return a < b ? a : b;
 9 }
10 
11 int edit(string str1, string str2)
12 {
13     int max1 = str1.size();
14     int max2 = str2.size();
15 
16     int **ptr = new int*[max1 + 1];
17     for(int i = 0; i < max1 + 1 ;i++)
18     {
19         ptr[i] = new int[max2 + 1];
20     }
21 
22     for(int i = 0 ;i < max1 + 1 ;i++)
23     {
24         ptr[i][0] = i;
25     }
26 
27     for(int i = 0 ;i < max2 + 1;i++)
28     {
29         ptr[0][i] = i;
30     }
31 
32     for(int i = 1 ;i < max1 + 1 ;i++)
33     {
34         for(int j = 1 ;j< max2 + 1; j++)
35         {
36             int d;
37             int temp = min(ptr[i-1][j] + 1, ptr[i][j-1] + 1);
38             if(str1[i-1] == str2[j-1])
39             {
40                 d = 0 ;
41             }
42             else
43             {
44                 d = 1 ;
45             }
46             ptr[i][j] = min(temp, ptr[i-1][j-1] + d);
47         }
48     }
49 
50     cout << "**************************" << endl;
51     for(int i = 0 ;i < max1 + 1 ;i++)
52     {
53         for(int j = 0; j< max2 + 1; j++)
54         {
55             cout << ptr[i][j] << " " ;
56         }
57         cout << endl;
58     }
59     cout << "**************************" << endl;
60     int dis = ptr[max1][max2];
61 
62     for(int i = 0; i < max1 + 1; i++)
63     {
64         delete[] ptr[i];
65         ptr[i] = NULL;
66     }
67 
68     delete[] ptr;
69     ptr = NULL;
70 
71     return dis;
72 }
73 
74 int main(void)
75 {
76     string str1 = "sailn";
77     string str2 = "failing";
78 
79     int r = edit(str1, str2);
80     cout << "the dis is : " << r << endl;
81 
82     return 0;
83 }

C版:

#include<stdio.h>
#include <string.h>
#include <malloc.h>

void backtracking(int**, char*, char*);    //回溯,计算出如何通过其中一个字符串的变换,得到另外一个字体串
int ** build_matrix(char*, char*);         //求编辑距离,返回一个已经填充好的矩阵
int trigle_min(int a, int b, int c);       //求三个数的最小值


int main()
{
    char* A = "GGATCGA";
    char* B = "GAATTCAGTTA";
    int** matrix = build_matrix(A, B);
    printf("A和B的编辑距离为:%d
", matrix[strlen(A)][strlen(B)]);
    backtracking(matrix, A, B);
    return 0;
}

//0 左上角 ,1上方,-1左边
int way(int i_t, int j_t, int i, int j)
{
    if (i - i_t == 1 && j - j_t == 1) {
        return 0;
    }
    if (i - i_t == 1 && j - j_t == 0) {
        return 1;
    }
    if (i - i_t == 0 && j - j_t == 1) {
        return -1;
    }
}

int** build_matrix(char* A, char* B)
{
    int m = strlen(A);
    int n = strlen(B);
    int** matrix = (int**)malloc(sizeof(int*)*(m + 1));
    int i, j;
    for (i = 0; i < m + 1; i++)
    {
        *(matrix + i) = (int*)malloc(sizeof(int)*(n + 1));
    }
    matrix[0][0] = 0;
    for (i = 1; i < n + 1; i++)
    {
        matrix[0][i] = i;
    }
    for (i = 1; i < m + 1; i++)
    {
        matrix[i][0] = i;
    }

    for (i = 1; i < m + 1; i++)
    {
        for (j = 1; j < n + 1; j++)
        {
            if (*(A + i - 1) == *(B + j - 1))
            {
                matrix[i][j] = matrix[i - 1][j - 1];
            }
            else
            {
                matrix[i][j] = trigle_min(matrix[i - 1][j], matrix[i][j - 1], matrix[i - 1][j - 1]) + 1;
            }
        }
    }
    for (i = 0; i <= m; i++)
    {
        for (j = 0; j <= n; j++)
        {
            printf("%d ", matrix[i][j]);
        }
        printf("
");
    }
    return matrix;
}

void backtracking(int** matrix, char* A, char *B) {
    int m = strlen(A);
    int n = strlen(B);
    int i = m;
    int j = n;
    int max = m > n ? m : n;
    char* p = (char*)malloc(sizeof(char)*m);
    char* q = (char*)malloc(sizeof(char)*m);
    int k = 0;
    while (i > 0 && j > 0)
    {
        if (*(A + i - 1) == *(B + j - 1))
        {
            *(p + k) = *(A + i - 1);
            *(q + k) = *(B + j - 1);
            --i;
            --j;
            ++k;
        }
        else
        {
            int i_t = 0;
            int j_t = 0;
            if (matrix[i][j - 1] >= matrix[i - 1][j])
            {
                i_t = i - 1;
                j_t = j;
            }
            else {
                i_t = i;
                j_t = j - 1;
            }
            if (matrix[i_t][j_t] >= matrix[i - 1][j - 1])
            {
                i_t = i - 1;
                j_t = j - 1;
            }
            /////////      
            int w = way(i_t, j_t, i, j);
            if (w == 0)  {
                *(p + k) = *(A + i - 1);
                *(q + k) = *(B + j - 1);
            }
            else if (w == -1) {
                *(p + k) = '-';
                *(q + k) = *(B + j - 1);
            }
            else {
                *(p + k) = *(A + i - 1);
                *(q + k) = '-';
            }
            ++k;
            i = i_t;
            j = j_t;
        }
    }
    if (i == 0) {
        *(q + k) = *(B + j - 1);
        *(q + k) = '-';

    }
    else {
        *(p + k) = *(A + i - 1);
        *(q + k) = '-';

    }
    for (i = max - 1; i >= 0; i--) {
        printf("%c", *(p + i));
    }
    printf("
");
    for (i = max - 1; i >= 0; i--) {
        printf("%c", *(q + i));
    }
}

int trigle_min(int a, int b, int c)
{
    int min = a < b ? a : b;
    return min < c ? min : c;
}

reference:编辑距离及编辑距离算法

                   编辑距离算法(LD)详解

原文地址:https://www.cnblogs.com/ranjiewen/p/6395241.html