動態規劃專欄:(一)編輯距離

LeetCode72. 編輯距離

一、題目描述

給定兩個單詞 word1 和 word2,計算出將 word1 轉換成 word2 所使用的最少操作數 。

你可以對一個單詞進行如下三種操作:

插入一個字符
刪除一個字符
替換一個字符
示例 1:

輸入: word1 = “horse”, word2 = “ros”
輸出: 3
解釋:
horse -> rorse (將 ‘h’ 替換爲 ‘r’)
rorse -> rose (刪除 ‘r’)
rose -> ros (刪除 ‘e’)
示例 2:

輸入: word1 = “intention”, word2 = “execution”
輸出: 5
解釋:
intention -> inention (刪除 ‘t’)
inention -> enention (將 ‘i’ 替換爲 ‘e’)
enention -> exention (將 ‘n’ 替換爲 ‘x’)
exention -> exection (將 ‘n’ 替換爲 ‘c’)
exection -> execution (插入 ‘u’)

二、題目理解

編輯距離算法在自然語言處理中是一種很基本的算法。題意爲將兩個不詳的單詞經過最少的變化次數,從而轉化成相同的單詞。

三、解題思路

這是一種很常見的二維動態規劃問題,這在一些複雜的字符串處理算法中是很常見的模型。

動態規劃問題總體上的思路是把一個大問題,以同一種模式一次又一次的向下拆分成小問題(子問題),再利用子問題的解,一層一層往上的幫助解決父問題。

所以一般動態規劃問題採用自底向上的思路來解決,本題也是。既然解題思路上有共性,那應該對這種二維動態規劃問題形成一種穩定的思維模式,因此下面的思路介紹會盡量脫離本題的內容,從而抽象泛化。

思路:
用一個二維數組dp[i][j]表示從word1的前i個字母到word2中前j個字母的距離。

兩個單詞w1和w2的比較(長度分別爲len1、len2),先拆分成w1從第1個字母到最後一個字母分別到w2的距離,再拆分成w1中每個字母到w2中從第一個到最後一個字母的距離;
反過來想,如果我知道了dp[len1-1][len2-1],那從dp[len1-1][len2-1]到dp[len1][len2]有四種情況:
1)w1和w2最後兩個字母相等,那dp[len1][len2]=dp[len1-1][len2-1]
2)w1和w2最後兩個字母不相等,且把w1最後一個字母去掉就使得w1=w2,所以dp[len1][len2]=dp[len1-1][len2]+1
3)w1和w2最後兩個字母不相等,且在w最後一個加上一個字母就使得w1=w2,所以dp[len1][len2]=dp[len1][len2-1]+1。

抽象出來即:
dp[i][j] 代表 word1 到 i 位置轉換成 word2 到 j 位置需要最少步數
當 word1[i] == word2[j],dp[i][j] = dp[i-1][j-1];
當 word1[i] != word2[j],dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1
其中,dp[i-1][j-1] 表示替換操作,dp[i-1][j] 表示刪除操作,dp[i][j-1] 表示插入操作。

以上就是主體思路。另外還需注意邊界條件,此處見代碼詳解。

四、代碼

#include <iostream>
#include <vector>
#include <math.h>
using namespace std;

class Solution {
public:
    int min(const int& a, const int& b, const int& c)
    {
        if ((a <= b && a <= c))
            return a;
        else if (b <= a && b <= c)
            return b;
        else 
            return c;        
    }

    int minDistance(const string& word1, const string& word2) 
    {
        int len1 = word1.length();
        int len2 = word2.length();
        vector<vector<int> > dp (len1+1,vector<int>(len2+1,0));//dp[i][j]表示word1從開始到第i個字母 到 word2從開始到第j個字母的轉換次數
        
        for (int i = 1; i <= len2; i++) dp[0][i] = dp[0][i-1]+1;//表示word1爲空時,到word2每個字母的距離
        for (int i = 1; i <= len1; i++) dp[i][0] = dp[i - 1][0]+1; //表示word2爲空時,到word1每個字母的距離
       

        for (int i = 1; i <= len1; i++)
        {
            for (int j = 1; j <= len2; j++)
                if (word1[i-1] == word2[j-1])//word1的第i個字母和word2的第j個字母相同
                    dp[i][j] = dp[i - 1][j - 1];
                else
                    dp[i][j] = min(dp[i - 1][j - 1], dp[i][j - 1], dp[i - 1][j])+1;             
        }
        return dp[len1][len2]; 
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章