插入、删除、修改操作 代价不等的 最短编辑距离 动态规化求法

直接上干货。

什么叫编辑距离?从字符串str1到字符串str2需要经过多少次编辑。每次编辑只能增加 / 删除 / 替换1个字符。计算出从str1到str2最小的编辑距离(Minimum Edit Distance),用此可作为两个字符串之间的相似度的衡量。

定义符号:

对于两个字符串strA, strB,它俩的长度分别记为lenA, lenB.

MED:最小的编辑距离(Minimum Edit Distance)

记C[i, j] = MED{ strA[0…i], strB[0…j] }. 即C数组中存储的是 以i j 结尾的两个字符串的最小编辑距离。

现在开始分析:

对于strA[0…lenA-1] 和 strB[0…lenB-1],

当计算C[i][j]时,我们认为C数组之前的值都已经计算好了,我们认为经过C[i-1][j-1]次最少次数的编辑之后,处理过的strA[0…i-1] 与处理过的 strB[0…j-1] 相等。

当计算C[i][j]时,不要去管i与j后面的字符串。

若strA[i] == strB[j],则C[i][j] = C[i-1][j-1].这是最简单的情况。

当strA[i] != strB[j]时,就要分情况讨论了:

1.删除strA[i],此时等价于要计算MED{ strA[0…i-1], strB[0…j] },显然,这就是C[i-1][j],此时C[i][j] = C[i-1][j] + cost_Del。

2.删除strB[j],此时等价于要计算MED{ strA[0…i], strB[0…j-1] },显然,这就是此时C[i][j] = C[i][j-1] + cost_Del。

3.将strA[i]修改为strB[j],此时等价于要计算MED{ strA[0…i-1], strB[0…j-1] },显然,此时C[i][j] = C[i-1][j-1] + cost_Edit。

4.将strB[j]修改为strA[i],此时等价于要计算MED{ strA[0…i-1], strB[0…j-1] },显然,此时C[i][j] = C[i-1][j-1] + cost_Edit。

5.将strB[j]添加到strA[i]之后,此时等价于要计算MED{ strA[0…i], strB[0…j-1] },显然,此时C[i][j] = C[i][j-1] + cost_Insert。

6.将strA[i]添加到strB[j]之后,此时等价于要计算MED{ strA[0…i-1], strB[0…j] },显然,此时C[i][j] = C[i-1][j] + cost_Insert。

由上述六种情况分析可知,只有当删除和插入的代价相同时,才可以合并这六种情况为三种。

状态转移方程为

注意,因为我们要用C[0][j]和C[i][0]来表示其中一个串为空时的最小编辑距离。所以C[i][j] = MED{ strA[0…i-1], strB[0…j-1] }

if(strA[i-1] == strB[j-1])
	C[i][j] = C[i-1][j-1];
else
{
	int C1 = C[i-1][j] + cost_Del;
	int C2 = C[i][j-1] + cost_Del;
	int C3 = C[i][j-1] + cost_Insert;
	int C4 = C[i-1][j] + cost_Insert;
	int C5 = C[i-1][j-1] + cost_Edit;
	C[i][j] = std::min(C1, C2, C3, C4, C5);
}

进行一下优化,空间复杂度可以降为O(min{lenA, lenB}).

定义C[2][N],其中N = min{lenA, lenB}。

if(strA[i-1] == strB[j-1])
	C[i&1][j] = C[(i-1)&1][j-1];
else
{
	int C1 = C[(i-1)&1][j] + cost_Del;
	int C2 = C[i&1][j-1] + cost_Del;
	int C3 = C[i&1][j-1] + cost_Insert;
	int C4 = C[(i-1)&1][j] + cost_Insert;
	int C5 = C[(i-1)&1][j-1] + cost_Edit;
	C[i][j] = std::min(C1, C2, C3, C4, C5);
}

leetcode上有类似的题,不过它默认是所有操作的代价都是相等的。所以只用比较三个值的大小即可。

https://leetcode.com/problems/edit-distance/

《插入、删除、修改操作 代价不等的 最短编辑距离 动态规化求法》

完整答案如下所示:

class Solution {
public:
    int minDistance(const string& word1, const string& word2) {
        int len1 = word1.size();
        int len2 = word2.size();
        if(0 == len1)
            return len2;
        else if(0 == len2)
            return len1;
        for(int j = 0; j <= len2; j++)
            C[0][j] = j;
        
        for(int i = 1; i < len1+1; i++)
        {
            C[(i-1)&1][0] = i-1;
            C[i&1][0] = i;
            for(int j = 1; j < len2 + 1; j++)
                if(word1[i-1] == word2[j-1])
                    C[i&1][j] = C[(i-1)&1][j-1];
                else
                {
                    int C1 = C[(i-1)&1][j-1] + 1;
                    int C2 = C[i&1][j-1] + 1;
                    C2 = C2 < C1? C2: C1;
                    int C3 = C[(i-1)&1][j] + 1;
                    C[i&1][j] = C3 < C2? C3 : C2;
                }
        }
        return C[len1&1][len2];
    }
    
private:
    int C[2][1000];
};

点赞