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]#回覆給的這個狀態需要多少次
逐步推進,穩紮穩打,學習也是大抵如此.