leetcode第115題 不同的子序列 python解法(用時48ms)
leetcode第115題 不同的子序列 python解法(用時48ms)
該題的意思就是在一個字串中找出與給定字串相同的子序列的個數。例如在S= "rabbbit"這個字串中找到與 T="rabbit"相等的子序列的個數。
問題分析
首先,這題最好使用動態規劃(該題也被劃分動態規劃這一類)。動態規劃時通過組合子問題的解而解決整個問題的。(演算法導論第192頁)。以上面的字串為例,如果想要找出"rabbit"的子序列個數,只要在"rabbbit"中所有的’t’前面找出"rabbi"出現的次數。同樣,為了找出"rabbi"在S中出現的次數,則要找出在S中所有的’i’前面"rabb"出現的次數。以此類推。所以總的來說,就是將T先分割成不同的字串,然後找出不同字串出現的次數,看上去是將題目複雜化了,其實運算量並不大。整個程式只是將S遍歷了一遍。為了記錄T不同長度字串出現的次數,也為了後續方便查詢,先建立了一個字典dictTimes,來記錄次數,對於T = “rabbit”,此字典內容為{‘r’: 0, ‘ra’: 0, ‘rab’: 0, ‘rabb’: 0, ‘rabbi’: 0, ‘rabbit’: 0
所以開始前也建立了一個滿足這樣關係的字典dictChars。這個字典的鍵是T中每一位的字元,值是T從頭到這一位的字串,具體為{‘r’: [’’], ‘a’: [‘r’], ‘b’: [‘ra’, ‘rab’], ‘i’: [‘rabb’], ‘t’: [‘rabbi’]}。這裡有兩點要注意:一,這裡值使用的是陣列,因為一個字元在T中出現的位置可能有多個,比如這裡的’b’,所以用一個數組來儲存;二,這裡第一位是‘r’,它前面是空字串。所以每當遍歷到’r’時,因為它是第一位,所以在dictTimes[r]直接加一。所以在dictTimes中加上一個鍵:"",值為1(此時dictTimes為{‘r’: 0, ‘ra’: 0, ‘rab’: 0, ‘rabb’: 0, ‘rabbi’: 0, ‘rabbit’: 0, ‘’: 1
遍歷過程
接下來我們以S = “rabbbit”, T = “rabbit"為例來遍歷一遍。首先,第一個字元時’r’,這時檢視dictChars字典,知道它前面是空字串,然後在dictTimes中知道空字串的次數是1,所以在dictTimes中將’r’的次數加一(加dictTimes[”"],每次遇到’r’都是這樣的操作),接下來遍歷到’a’,從dictChars知道它在T中前面的字串是"r",所以在dictTimes中看"r"的值是否等於零。如果等於零,說明"r"沒有出現過,這樣的話此處的’a’就沒有用(因為湊不出有用的"ra")。但是我們的例子中dictTimes[r]等於1,所以此處的’a’有用,即將dictTimes中"ra"的值也設為1。接下來遍歷到第一個’b’,我們此時要遍歷dictChars[b]的值,看它們在前面出現的次數,這裡有一點比較關鍵,就是對於有多個值的陣列,一定要從後向前遍歷。
按照上述的方法,一步步遍歷下去,當遍歷到’t’時,我們看dictTimes[rabbi]的值(等於3),所以最後“rabbit”出現的次數就是等於3。最終的結果只要返回dictTimes[rabbit]就OK了。
這裡用的例子比較簡單,但複雜的情況也是這樣的分析,不在囉嗦。
原始碼(Python)
class Solution(object):
def numDistinct(self, s, t):
"""
:type s: str
:type t: str
:rtype: int
"""
lengthT = len(t)
dictTimes = {}
dictChars = {}
for i in range(lengthT):
if t[i] in dictChars:
dictChars[t[i]].append(t[:i])
else:
dictChars[t[i]] = [t[:i]]
for i in range(1, lengthT+1):
dictTimes[t[:i]] = 0
dictTimes[''] = 1
for char in s:
if char in dictChars:
for i in dictChars[char][::-1]:
if dictTimes[i] > 0:
dictTimes[i+char] += dictTimes[i]
return dictTimes[t]