Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.
You have the following 3 operations permitted on a word:
- Insert a character
- Delete a character
- Replace a character
Example 1:
Input: word1 = "horse", word2 = "ros" Output: 3 Explanation: horse -> rorse (replace 'h' with 'r') rorse -> rose (remove 'r') rose -> ros (remove 'e')
这道题目大致要求就是求一个字符串通过几次这样增加、删减、替换三个操作转换为目标字符串。题目非常清楚,但是实际上却难住了我一段时间,刚开始我准备用递归来解决这个问题,但是后来实践上感觉效率太慢了。然后看了网上各位大神的博客,才发现有一个专门的算法叫Levenshtein距离算法(编辑距离这个概念由俄罗斯科学家Vladimir Levenshtein在1965年提出来的,所以也叫 Levenshtein 距离,用来做DNA分析,拼字检测,抄袭识别等等)。这个算法简单通过一个矩阵就得到结果,重新刷新了我的认知,更能看出算法的厉害。
算法详解
首先假设我们可使用d[i][j]表示将一个字符串s[1....i]转换为t[1....j]所需操作。从最简单的情况开始考虑,当其中的i为0,s为空字符串时,只需增加j次字符就可得到字符串t,d[0][j]为j,同理当字符串t为空时,只需删减i个字符,d[i][0]为i。
然后我们以此来类推一般的情况,我们现在假设已经通过最少的操作使得s转换为t之差一个字符,因为有三种操作所以分为三种情况讨论:
(1)m个操作内将s[1....i]转换为t[1....j-1],这时只需增加一个字符就可满足,总的操作为m+1;
(2)n个操作内将s[1....i-1]转换为t[1....j],这时只需删减一个字符就可满足,总的操作为n+1;
(3)k个操作内将s[1....i-1]转换为t[1....j-1],这时只需替换一个字符就可满足,总的操作为k+1,当s[i] == t[j]时,则无需替换只要k个操作。
所以最后我们就可得到总的最少的操作数为{m+1, n+1, (k+1)||k}中最小的一个。
实现步骤
(1)首先我们创建一个(x+1)*(y+1)的矩阵(其中x,y分别为两个字符串的长度),保存将一个字符串s[1....x]转换为t[1....y]所需操作数;
(2)初始化这个矩阵的第一行,第一列分别为0到x和0到y,结果如下:
h | o | r | s | e | ||
0 | 1 | 2 | 3 | 4 | 5 | |
r | 1 | |||||
o | 2 | |||||
s | 3 |
(3)将字符串t中每一个字符与字符串s中的所有字符比较一次,假设其中字符串s第i个等于字符串t第j个字符,用tmp记为0,否则记为0。然后矩阵中d[i][j]为{d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1]+tmp}中最小的。
h | o | r | s | e | ||
0+tmp | 1+1 | 2 | 3 | 4 | 5 | |
r | 1+1 | 1 | ||||
o | 2 | 2 | ||||
s | 3 | 3 |
h | o | r | s | e | ||
0 | 1 | 2 | 3 | 4 | 5 | |
r | 1 | 1 | 2 | |||
o | 2 | 2 | 1 | |||
s | 3 | 3 | 2 |
依次类推得到:
h | o | r | s | e | ||
0 | 1 | 2 | 3 | 4 | 5 | |
r | 1 | 1 | 2 | 2 | 3 | 4 |
o | 2 | 2 | 1 | 2 | 3 | 4 |
s | 3 | 3 | 2 | 2 | 2 | 3 |
最后d[x][y]为所求的结果。
我们根据此来实现我们的程序:
int minThree(int a, int b, int c) {
int min = a < b ? a : b;
min = min < c ? min : c;
return min;
}
int minDistance(char* word1, char* word2) {
int size1 = strlen(word1);
int size2 = strlen(word2);
int** dv = (int**)malloc(sizeof(int*) * (size1 + 1));
int i, j;
for(i=0; i<=size1; i++) {
dv[i] = (int*)malloc(sizeof(int) * (size2 + 1));
dv[i][0] = i;
}
for(j=0; j<=size2; j++) {
dv[0][j] = j;
}
for(i=1; i<=size1; i++) {
for(j=1; j<=size2; j++) {
if(word1[i-1] == word2[j-1]) {
dv[i][j] = dv[i-1][j-1];
}
else {
dv[i][j] = minThree(dv[i-1][j] + 1, dv[i][j - 1] + 1, dv[i-1][j-1] + 1);
}
}
}
return dv[size1][size2];
}
运行结果