91. Decode Ways
這題的題目是如果說給出一串字串,由數字組成,如果要轉換成英文,可以轉換成幾種方式?其中比較特別的就是如果兩個數字剛好不是零為開頭,那像是 "11"
就可以變成 aa
或是 k
像這樣的題目滿像是走樓梯問題的,一次可以走一步或兩步,不過呢,需要注意的是什麼樣的情況下可以走,以及邊界的情況。
既然是走樓梯問題,那就可以先用走樓梯問題的方式來建構題目。
class Solution:
def numDecodings(self, s: str) -> int:
def recursive(index: int) -> int:
# TODO
ans = recursive(index + 1)
ans += recursive(index + 2)
return ans
return recursive(0)
我先一算出走一步的有幾種組合,再加上走兩步的有幾種組合,那應該就是答案了吧?很接近,但是我們要注意的是只有 10 ~ 26 是有機會可以當作兩步走的。
class Solution:
def numDecodings(self, s: str) -> int:
def recursive(index: int) -> int:
# TODO
ans = recursive(index + 1)
if 10 <= int(s[index : index + 2]) <= 26:
ans += recursive(index + 2)
return ans
return recursive(0)
到了這裡,我們會發現這個遞迴會無限的走下去,因為沒有終止條件。
有哪些終止條件呢?
第一種情況 s[index] == '0'
,以 '10' 來看,先遞迴 '1'
接著是 '0'
,走到 '0'
的瞬間,沒有英文的組合,所以要退回,返回零種組合,這時候走一步的情況無法,接著因為 10
符合條件,所以再走往前走二步,這時候會發生 index == 2
越界的情況。而能夠發生這種情況的,都因為一定會符合這個條件:10 <= int(s[index : index + 2]) <= 26
,所以會促成下一個終止條件。
第二種情況 index == len(s)
這個會比較難想一點,需要靠上面的例子來想出來,返回一種組合。
第三種情況 index == len(s) - 1
這個很簡單,因為當條件成立,我們走到了最後一位,且也不是 '0'
,所以說可以直接返回一種組合。
而且這題最困難的一個地方,是這三個條件,是有順序性的,一定要是按照以下的方式:
class Solution:
def __init__(self):
self.memo = {}
def numDecodings(self, s: str) -> int:
def recursive(index: int) -> int:
# 要先處理越界,不然下一個條件會報錯
if index == len(s):
return 1
# 再來要看是不是零,因為有可能最後一個數字是零
if s[index] == '0':
return 0
# 沒有越界,也不是零,才是真的到達了終點
if index == len(s) - 1:
return 1
ans = recursive(index + 1)
if 10 <= int(s[index:index + 2]) <= 26:
ans += recursive(index + 2)
return ans
return recursive(0)
到了這裡,就會發現其實會有很多重疊的子問題,例如:1111111
這樣的組合,就會有很多的重疊子問題,但因為我們有了遞迴的關係,我們可以直接用自頂向下的方式來記憶。
自頂向下
class Solution:
def numDecodings(self, s: str) -> int:
memo = {}
def recursiveWithMemo(index: int):
if index in memo:
return memo[index]
if index == len(s):
return 1
if s[index] == '0':
return 0
if index == len(s) - 1:
return 1
ans = recursiveWithMemo(index + 1)
if 10 <= int(s[index : index + 2]) <= 26:
ans += recursiveWithMemo(index + 2)
memo[index] = ans
return memo[index]
return recursiveWithMemo(0)