每日一題,每日一練。22編輯距離(被迫用動態規劃時,沒有一個子問題是無辜的)

72.編輯距離
給你兩個單詞 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’)

這是一道看上去非常難的題但是轉述起來非常簡單的題,我們現在把這道題轉述一下,
給定兩個字符串word1和word2,你可以往word1或word2中添加字符,或者修改word1的字符(這點也和修改word2的字符等價),求使得word1和word2相等的最小操作。
是的,由於這題並不需要最終字符串的最小長度,你可以將刪除視爲對另一個字符串的增加,於是這個問題變成了一個單向的問題,那麼既然是單向,在相等的前一時刻我們是怎麼操作的呢,我們有三種選擇:
1:我們給word1加上一個字符,之後word1和word2就相等辣
2:我們給word2加上一個字符,之後。。。。。。。相等辣
3:我們換掉一個字符,之後。。。。。。。。辣

很顯然,最終時刻相等的操作是每個時刻的相等的操作的集合,因此我們需要利用動態規劃,於是我們先建立dp及邊框(以1爲例)

這裏需要記住三點
1:每個位置的值是當前word1,word2需要達到相同經歷的操作次數,也就是每個位置在經歷這麼多操作次數後已經達到了相等狀態
2:在新的字符引入後,我們需要思考如何達到新的相等,比如如果word1多一個h,我們要麼給對面加一個h,要麼在對面的尾端(因爲前面的字符串已經處理完成了)改一個h
3(重點):我們需要知道是在什麼基礎狀態下產生了新的待處理字符,這決定了我們之前是哪個狀態的累計操作次數
邊框的建立很簡單因爲最簡單的是一個字符串爲空的時候,我們只需要增加這個字符串就行了。做的操作次數就是字符串的長度,然後我們對每個部分進行來源查找,這裏我們以word1=“h”,word2=“r”舉例,也就是本表格中3行3列的位置,我們比較上一時刻的相等狀態有三種辦法,請記住我們只有增加和改動,刪除已經等同於增加另一個。

1:可以看成由2行3列,也就是在h,空可以在一步操作到相等的狀況下在word2又的尾端又需要多處理了一個r,變到了這裏,那麼相應的,我們需要給對面加一個r,多出一步操作
2:也有可能是3行2列,在空,r可以在一步操作到相等的狀況下的時候,word1的尾端由需要多處理一個h,那麼相應的,我們需要給對面加一個h,多出一步操作。
3:也有可能在2行2列,在空,空可以不進行操作相等的時候,兩邊同時多出一個字符分別爲h和r,這個時候我們進行一步修改操作,讓h變成r,用這一步來形成新的相等。

由於我們只進行一個字符的改動,因此只存在這三種情況(因爲我們前文提到每一步的操作只有三種操作,也只有這三種可能通過一步操作達成新的相等)可以在一步改動中到達新的相等狀態,這也是動態規劃很重要的一點,就是逐步推進。即在之前已經確認狀況的情況下,當前可能是一下三種狀況之一。

我們需要多處理一個word1字符
我們需要多處理一個word2字符
我們需要多處理一個word1字符和word2字符,用修改讓他們相等

那麼我們比較這三種方式那種更好,實際上的狀態轉移就是

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

也就是在當前時刻,我們需要判斷這個狀態是比之前多處理word1的一個字符(多處理一行),還是比之前多處理word2的一個字符(多處理一列),還是word1和word2都比之前增加一個字符,而這個字符通過改變處理,這三種方式那個方式形成新的操作次數最少,注意這三種之前並不是一種狀態,我們只用這三種是因爲只有這三種形成的新狀態符合動態規劃,即一步操作可以形成新的相等

當然,有的人可能發現了問題,在增加操作不會有什麼問題,因爲如果我們視爲這個狀態是比之前增加一個字符,我們只要在另一個字符串也加一個字符就行了,這是固定的一步,但如果我們視爲處理狀況多兩個字符,那麼當出現表4行4列,word1=“ho”,而word2=“ro”時,如果我們把它視爲第三種情況,即表3行3列word1=“h”而word2=‘r’時新增兩個待處理字符通過修改解決的情況,我們會發現新增的字符並不需要修改,o和o是相等的,因此,我們需要對動態轉移方程的第三步重新設定一個值check,如果新加入的兩個字符是相等的,那麼check=0,否則=1,新的DP方程構建如下

dp[ii][i]=min(dp[ii-1][i]+1,dp[ii][i-1]+1,dp[ii-1][i-1]+check)

我們再來回顧一下整道題的步驟

建立dp邊界
從都有字符串的時候開始,推導之前的基礎狀況
更新當前狀態的最小操作次數

代碼如下:

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        dp=[[0 for i in range(len(word2)+1)]for i in range(len(word1)+1)]
        for i in range(len(dp[0])):#建立邊界
            dp[0][i]=i
        for i in range(len(dp)):#建立邊界
            dp[i][0]=i
        for ii in range(1,len(dp)):
            for i in range(1,len(dp[0])):
                if(word1[ii-1]!=word2[i-1]):
                    check=1
                else:
                    check=0
                dp[ii][i]=min(dp[ii-1][i]+1,dp[ii][i-1]+1,dp[ii-1][i-1]+check)#基於前三種狀態看從哪邊過來更好
        return dp[-1][-1]#回覆給的這個狀態需要多少次

逐步推進,穩紮穩打,學習也是大抵如此.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章