经典题:编辑距离

目录

引入

题目

示例

 数据规模与说明​编辑

思路解析

策略1:插入

策略2:删除

策略3:替换

AC代码(leetcode)

AC代码(洛谷)

复杂度


引入

Leetcode 72.编辑距离

洛谷 P2758编辑距离

从各大OJ平台对其的收录,我们可以知道这道题是多么的经典。闲言少叙,书归正传。接下来我们进入正题。

题目

示例

 数据规模与说明

 

思路解析

抓取关键字 “最少操作次数” ,所以我们很快反应过来这是一题最优化的题目。面对这种题目,不妨先找找 “最优子结构”

假设我们获取到一个 “最优子结构”,即从 word_{1}[1...i] 到 word_{2}[1...j] 的最小操作次数。

PS:从 word_{2}[1...j] 到 word_{1}[1...i] 的最小操作次数,这无伤大雅。

值得注意的是,这个时候我们并不关心我们假设的最优子结构如何获取得到的。只是单纯地反应我们总有方法能够获取最优子结构,所以假设我们处理的情形是可以获取得到最优子结构后。

接下来,我们就要思考如果我们现在要获取 word_{1}[1...i] 到 word_{2}[1...j+1] 的最小操作次数。此时,依据题意我们拥有三种操作(策略)。在变换之前,首要的就是分析各个策略对于的变换条件。只有得知了变换条件,我们才能够合理地运用策略来获取正确的最优子结构。

策略1:插入

首先,插入操作对于 word_{1}[i] 与 word2_{2}[j + 1] 的大小关系没有要求。其次他对两个字符串的长度也没有做出要求。也就是说,我们可以在任何情况下采取这个策略进行尝试。

再者,如果我们对 word_{1} 进行插入操作,那么一定有 word_{1}[i + 1] = word_{2}[j + 1],所以我们只要知道 word_{1}[1...i] 到 word_{2}[1...j] 的最小操作次数,而这是我们可以知道的。

策略2:删除

首先,删除操作跟插入操作一样,是可以在任何情况执行的。换一种角度来说,如果对 word_{1} 进行插入操作,其实就是对 word_{2} 进行删除操作。

那么,我们对 word_{1} 进行删除操作,也就是删除  word_{1}[i] ,我们只需要知道 word_{1}[1...i - 1] 到 word_{2}[1...j + 1] 的最小操作次数即可。

策略3:替换

替换操作就与前两种操作体系不同,替换操作对 word_{1}[i] 与 word2_{2}[j + 1] 的大小关系有所要求。如果,二者相等就可以不执行,否则就可以尝试替换操作。相同的,无论执不执行,我们都需要知道  word_{1}[1...i - 1] 到 word_{2}[1...j] 的最小操作次数。

得知了三种策略的使用尝试条件,那么我接下来就可以使用他们来获取正确的最优子结构。他们对应代码如下:

if( word1[i - 1] != word2[j - 1])

        dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;

 else

        dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1] - 1) + 1;//dp[i-1][j-1] - 1是为了抵消式子外+1的作用

通过上述的分析,我们也得知了如果要获取 dp[i][j] 则需要知道 dp[i-1][j], dp[i][j-1], dp[i-1][j-1] 三个状态值。其中 dp[i][j] 表示从 word_{1}[1...i] 到 word_{2}[1...j] 的最小操作次数。

以case1为示例的运行表(未初始化)
#ROS
#1111
H1/12/1......
O12
R1...
S1...
E1...

注:#表示空字符串,2表示我们在计算的,1表示我们需要获取的

通过打标我们知道,我们需要知道 第 0 行 和 第 0 列的情况。而这一部分的最小操作次数是显而易见的。# 与 word_{2}[1...j] 的最小操作次数为 j 次。反之,同理。

所以,我们获取到初始化部分与其值。故而有以下代码:

 for (int i = 1; i <= len_word1; ++i) dp[i][0] = i;

 for (int i = 1; i <= len_word2; ++i) dp[0][i] = i;

最后我们获取到AC代码。

AC代码(leetcode)

class Solution {
public:
    int minDistance(string word1, string word2) {
        int len_word1 = word1.size();
        int len_word2 = word2.size();

        vector<vector<int>> dp(len_word1 + 1, vector<int>(len_word2 + 1, 0));
        for (int i = 1; i <= len_word1; ++i) dp[i][0] = i;
        for (int i = 1; i <= len_word2; ++i) dp[0][i] = i;

        for(int i = 1; i <= len_word1; ++i) {
            for (int j = 1; j <= len_word2; ++j) {
                if( word1[i - 1] != word2[j - 1])
                    dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
                else 
                    dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1] - 1) + 1;
            }
        }

        return dp[len_word1][len_word2];
    }
};

AC代码(洛谷)

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Solution {
public:
    int minDistance(string word1, string word2) {
        int len_word1 = word1.size();
        int len_word2 = word2.size();

        vector<vector<int>> dp(len_word1 + 1, vector<int>(len_word2 + 1, 0));
        for (int i = 1; i <= len_word1; ++i) dp[i][0] = i;
        for (int i = 1; i <= len_word2; ++i) dp[0][i] = i;

        for(int i = 1; i <= len_word1; ++i) {
            for (int j = 1; j <= len_word2; ++j) {
                if( word1[i - 1] != word2[j - 1])
                    dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
                else 
                    dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1] - 1) + 1;
            }
        }

        return dp[len_word1][len_word2];
    }
};

int main() {
    string word1, word2;
    cin >> word1;
    cin >> word2;
    
    Solution A;
    cout << A.minDistance(word1, word2);
    return 0;
}

复杂度

时间复杂度 O(len_word1 * len_word2), 空间复杂度同时间复杂度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值