问题:编辑距离
题目描述
给出字符串A和B,对A进行三种操作:插入、删除或替换一个字符,问由A修改为B最少要几步?
分析
定义状态
乍一看题目,感觉十分复杂,但是冷静分析一下,发现还是可以找到动态规划的影子。虽然字符串A与B之间长度不一定相同,并且可能存在着很多复杂的转化操作,但是还是可以按照对应位置进行状态的转移,如果现在考虑A的第i位字符在向B的第j位字符进行转化,可能会进行添加、删除、替换三种操作,所以我们可以将状态定义为:表示字符串A在第i位想要与字符串B在第j位实现匹配所需要执行的最小编辑次数。
状态转移
根据状态定义时的分析,在字符串A的第i位与字符串B的第j位进行匹配时,其最小编辑次数可以由以下几种情况转化而来
(1)如果需要进行字符串A的添加操作,则为
(2)如果需要进行字符串A的删除操作,则为
(3)如果需要进行字符串A的替换操作,则为
对于这三种情况,分别会计算出不同的编辑次数,只需要取其中最小值作为即可。
边界条件
考虑到边界情况,假设A、B其中一个字符串为空,则,,所求值为
求解方法
动态规划(打表法)
int minDistance(string a, string b) {
int lengthA = a.length();
int lengthB = b.length();
// 如果原始字符串a或者目标字符串b为空串,则编辑距离为删除(添加)lengthA(lengthB)个字符
if (lengthA * lengthB == 0) {
return (lengthA + lengthB);
}
// 初始化状态变量(二维数组)
vector<vector<int>> distance(lengthA + 1, vector<int>(lengthB + 1, 0));
for (int i = 0; i < lengthA + 1; i++) {
distance[i][0] = i;
}
for (int j = 0; j < lengthB + 1; j++) {
distance[0][j] = j;
}
// 使用打表法计算状态数组中每个状态值(到当前字符的编辑距离长度)
for (int i = 1; i < lengthA + 1; i++) {
for (int j = 1; j < lengthB + 1; j++) {
if (a[i - 1] != b[j - 1]) {
distance[i - 1][j - 1]++;
}
distance[i][j] = min(distance[i - 1][j] + 1, min(distance[i][j - 1] + 1, distance[i - 1][j - 1]));
}
}
return distance[lengthA][lengthB];
}
常规的打表法,需要注意定义存储表时长度要加1(当然我是喜欢与序号同步,如果你喜欢从0开始维护就忽略)。
递归法代码由于懒惰就不放了
时间复杂度
很明显,搜索过程中产生的时间复杂度为。