教科书般经典的动归题目,也可以看作是地图寻路问题。例如word1="ceab",word2="abc",构造如下地图。其中"^"表示起点,"$"表示终点,则题目转化成了寻找一条从起点到终点的最短路径。
a b c <- word2 word1 -> c ^ . . e . . . a . . . b . . $
对于地图上任意坐标为(i,j)的点,可以:
1. 向右走一步,表示跳过word2的一个字符,即删除一个word2的字符或向word1插入一个字符。
2. 向下走一步,表示跳过word1的一个字符,即删除一个word1的字符或向word2插入一个字符。
3. 向右下走一步,表示同时跳过word1和word2的一个字符,即修改一个word1或word2的字符,如果这两个字符本来就相等则不用修改。
令distance[i][j]表示从该点到终点的最短距离(也就是word1[i..n]和word2[j..m]的编辑距离),则有递推公式:
distance[i][j] = min{distance[i+1][j] + 1, distance[i][j+1] + 1, distance[i+1][j+1] + (word1[i] == word2[j] ? 0 : 1)}
具体实现中,由于递推公式只用到了相邻层的结果,所以可以压缩状态,用一维数组保存distance。
代码:
1 int minDistance(string word1, string word2) { 2 int len1 = word1.length(); 3 int len2 = word2.length(); 4 5 if (len1 < len2) 6 return minDistance(word2, word1); 7 if (len2 == 0) 8 return len1; 9 10 vector<int> distance(len1 + 1, 0); 11 for (int j = len2; j >= 0; j--) 12 distance[j] = len2 - j; 13 int rd = 0; // 表示distance[i+1][j+1] 14 15 for (int i = len1 - 1; i >= 0; i--) { 16 rd = distance[len2]; 17 distance[len2] = len1 - i; 18 for (int j = len2 - 1; j >= 0; j--) { 19 int tmp = distance[j]; 20 distance[j] = min(distance[j] + 1, distance[j + 1] + 1); 21 distance[j] = min(distance[j], rd + (word1[i] == word2[j] ? 0 : 1)); 22 rd = tmp; 23 } 24 } 25 26 return distance[0]; 27 }