动态规划典中典之编辑距离

135 阅读2分钟

给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
---插入一个字符
---删除一个字符
---替换一个字符

  1. 确定是最值问题,解题过程可枚举,考虑动态规划
  2. 最后一步:假定经过最少的操作次数我们把word1转换成了word2,假定最后一步之前的单词为wordX
    • 如果wordX比word2短一个字符,则word2必然是wordX增加一个字符得到的;
    • 如果wordX比word2长一个字符,则word2必然是wordX删除一个字符得到的;
    • 如果wordX和wordX一样长,则word2要么就是wordX,要么就是wordX替换一个字符得到的。
  3. 确定状态数组dp[i][j]为word1转化为word2的最小操作数,其中i为word1的长度,j为word2的长度
  4. 确定状态转移方程:
    • 最后一步是增加一个字符:dp[i][j] = dp[i-1][j]+1
    • 最后一步是删除一个字符:dp[i][j] = dp[i+1][j]+1
    • 最后一步是替换一个字符:dp[i][j] = dp[i-1][j-1]+1
    结果就是三者中最小的
  5. 确定边界:word1为空串s[0][j] = j;word2为空串s[i][0] = i;
  6. 确定初值:最坏情况下,word1,word2没有一个相同字符,则需要替换掉较短字符串,再把剩下的字符加入或删除。因此操作次数为两个字符串长度的大者。
class Solution {
public:
    int minDistance(string word1, string word2) {
        int src_len = word1.length();
        int dst_len = word2.length();
        // 边缘场景
        if (src_len == 0 || dst_len == 0) {
            return src_len == 0 ? dst_len : src_len;
        }

        // DP数组
        vector<vector<int>> min_instance(src_len + 1, vector<int>(dst_len + 1));

        // 边界条件:空串
        for (int i = 0; i < src_len + 1; i++) {
            min_instance[i][0] = i;
        }
        for (int j = 0; j < dst_len + 1; j++) {
            min_instance[0][j] = j;
        }

        // DP => 最后一步
        for (int i = 1; i < src_len + 1; i++) {
            for (int j = 1; j < dst_len + 1; j++) {
                int min_add = min_instance[i - 1][j] + 1;
                int min_erase = min_instance[i][j - 1] + 1;
                int min_replace = min_instance[i - 1][j - 1];
                if (word1[i - 1] != word2[j - 1])
                    min_replace += 1;
                min_instance[i][j] = min(min_add, min(min_erase, min_replace));

            }
        }
        return min_instance[src_len][dst_len];
    }
};