Day 27 回溯:39|40. 組合總和 I II, 131. 分割回文串

39. 組合總和

  • 思路
    • example
    • 回溯:深度由sum_ vs target控制枝恋,寬度由startIndex控制。
    • candidates中無重復(fù)元素
      • 不需要考慮同層去重
    • 無限制重復(fù)被選取 (有放回)
      • startIndex取當(dāng)前位置
    • "去重“:排序 + startIndex

無重可復(fù)選
注意:本題不需預(yù)先排序锌仅。

  • 剪枝
    • 模向 (for 循環(huán)內(nèi))
    • 縱向 (遞歸函數(shù)base case)
  • 2 <= candidates[i] <= 40
  • 復(fù)雜度. 時間:O(?), 空間: O(?)
class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(candidates, start, sum_):
            if sum_ == target: 
                res.append(path[:])
                return 
            for i in range(start, len(candidates)):
                if sum_ + candidates[i] > target:
                    break  
                path.append(candidates[i])
                sum_ += candidates[i]
                backtrack(candidates, i, sum_)
                sum_ -= candidates[i]
                path.pop()
        candidates.sort()
        res, path = [], []
        backtrack(candidates, 0, 0)
        return res 
class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(candidates, start, sum_):
            if sum_ > target: 
                return 
            if sum_ == target:
                res.append(path[:])
                return 
            for i in range(start, len(candidates)):
                # 在這里提早剪枝更好一些
                path.append(candidates[i])
                sum_ += candidates[i] 
                backtrack(candidates, i, sum_)
                sum_ -= candidates[i] 
                path.pop()
        res, path = [], []
        backtrack(candidates, 0, 0)
        return res 
class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(start):
            if path and sum(path) == target:
                res.append(path[:])
                return 
            if path and sum(path) > target:
                return 
            for i in range(start, len(candidates)):
                path.append(candidates[i])
                backtrack(i) # !!! 可重復(fù)取
                path.pop()
        res, path = [], []
        backtrack(0)
        return res 
  • 另一種寫法
    • 深度:逐個遍歷candidates
    • 寬度:每個candidate選取可能性:0大刊,1劳景,2,。蝙搔。钓辆。
TBA

40. 組合總和 II

  • 思路
    • example
    • candidates有重復(fù)元素
      • 同層去重
if i > start and candidates[i] == candidates[i-1]:
                    continue
  • candidates 中的每個數(shù)字在每個組合中只能使用 一次
  • 復(fù)雜度. 時間:O(?), 空間: O(?)
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(candidates, start, sum_):
            if sum_ == target:
                res.append(path[:])
                return 
            for i in range(start, len(candidates)):
                if i > start and candidates[i] == candidates[i-1]:
                    continue 
                if sum_ + candidates[i] > target:
                    break  
                path.append(candidates[i])
                sum_ += candidates[i]
                backtrack(candidates, i+1, sum_)
                sum_ -= candidates[i]
                path.pop()
        res, path = [], []
        candidates.sort()
        backtrack(candidates, 0, 0)
        return res 
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(candidates, start, sum_): # sum_ 還沒有計入start位置的貢獻
            if sum_ > target: # 剪枝
                return 
            if sum_ == target: # 終止條件 
                res.append(path[:])
                return 
            for i in range(start, len(candidates)): 
                if i > start and candidates[i] == candidates[i-1]: # 去重
                    continue 
                # 也可以在這里剪枝
                path.append(candidates[i])
                sum_ += candidates[i]
                backtrack(candidates, i+1, sum_) #  無放回情況
                sum_ -= candidates[i]
                path.pop()
        candidates.sort() # candidates中有重復(fù)
        res, path = [], []
        backtrack(candidates, 0, 0)
        return res 
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(start):
            if path and sum(path) > target:
                return 
            if path and sum(path) == target:
                res.append(path[:])
                return 
            for i in range(start, len(candidates)):
                if i > start and candidates[i] == candidates[i-1]: # !!!
                    continue 
                path.append(candidates[i])
                backtrack(i+1)
                path.pop()
        candidates.sort() # !!!
        res, path = [], []
        backtrack(0)
        return res 
class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        def backtrack(start, sum_):
            if sum_ > n:
                return 
            if len(path) == k:
                if sum_ == n:
                    res.append(path[:]) 
                return 
            for i in range(start, 9-(k-len(path))+2):
                path.append(i)  
                sum_ += i  
                backtrack(i+1, sum_)
                sum_ -= i  
                path.pop()
        res, path = [], []
        backtrack(1, 0)
        return res 
  • 小結(jié):組合
    • 去重 (sort, startIndex)
    • 剪枝
      • 個數(shù)
      • target, sum_, ...
    • 有放回 vs 無放回
      • startIndex: i vs i + 1
  • 桶裝球 vs 球裝桶
    • 桶的限制剪验?球的限制?
    • 桶裝球:桶視角前联。深度=桶功戚,寬度=球(可選范圍,規(guī)格似嗤,限制啸臀,每次選一個球)
    • 球裝桶:球視角。深度=球烁落,寬度=桶(桶的體積乘粒,規(guī)格,限制伤塌,可能取多個球)

131. 分割回文串

  • 思路
    • example
    • 返回 s 所有可能的分割方案: 暴力回溯谓厘。
      • 切割 (轉(zhuǎn)化為組合問題)
      • 判斷回文 (雙指針)


  • 暴力回溯:
    • 桶裝球
    • 深度:切割位置 (子串起始位置startIndex),桶
    • 寬度:切割位置 (子串結(jié)束位置)寸谜,球
  • 復(fù)雜度. 時間:O(n 2^n), 空間: O(1)
    • worst case: s = ‘a(chǎn)aaaaaaaa’
class Solution:
    def partition(self, s: str) -> List[List[str]]:
        def isPalindrome(s, left, right): # [left, right]
            while left < right:
                if s[left] == s[right]:
                    left += 1
                    right -= 1
                else:
                    return False 
            return True
        def backtrack(s, start):
            if start == len(s):
                res.append(path[:])
                return 
            for i in range(start, len(s)):
                if isPalindrome(s, start, i): 
                    path.append(s[start:i+1])
                    backtrack(s, i+1)
                    path.pop()
        res, path = [], []
        backtrack(s, 0)
        return res  
  • 優(yōu)化: 動規(guī)預(yù)處理(O(n^2)):計算所有子串回文與否 (isPalindrome[i][j]: s[i], ..., s[j])
    • 時間:O(n 2^n)
class Solution:
    def partition(self, s: str) -> List[List[str]]:
        def backtrack(s, start):
            if start == len(s):
                res.append(path[:])
                return 
            for i in range(start, len(s)):
                if isPalindrome[start][i] == True:
                    path.append(s[start:i+1])
                    backtrack(s, i+1)
                    path.pop()
            
        n = len(s)
        isPalindrome = [[False for _ in range(n)] for _ in range(n)]
        for i in range(n-1, -1, -1): # 逆序
            for j in range(i, n):
                if s[i] == s[j]:
                    if j - i <= 1:
                        isPalindrome[i][j] = True 
                    else:
                        isPalindrome[i][j] = isPalindrome[i+1][j-1]
        res, path = [], []
        backtrack(s, 0)
        return res 
class Solution:
    def partition(self, s: str) -> List[List[str]]:
        def backtrack(start):
            if start == len(s):
                res.append(path[:])
                return 
            for i in range(start, len(s)):
                if dp[start][i]:
                    path.append(s[start:i+1])
                    backtrack(i+1)
                    path.pop()
        res, path = [], []
        dp = [[False for _ in range(len(s))] for _ in range(len(s))]
        for i in range(len(s)-1, -1, -1):
            for j in range(i, len(s)):
                if i == j:
                    dp[i][j] = True 
                elif j - i + 1 == 2:
                    if s[i] == s[j]:
                        dp[i][j] = True
                else:
                    dp[i][j] = (s[i] == s[j]) and dp[i+1][j-1]
        backtrack(0)
        return res 
  • 進一步優(yōu)化
    • 動規(guī), 記憶化搜索, 分治?
TBA
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末竟稳,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子熊痴,更是在濱河造成了極大的恐慌他爸,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件果善,死亡現(xiàn)場離奇詭異诊笤,居然都是意外死亡,警方通過查閱死者的電腦和手機巾陕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進店門讨跟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鄙煤,你說我怎么就攤上這事晾匠。” “怎么了梯刚?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵凉馆,是天一觀的道長。 經(jīng)常有香客問我,道長澜共,這世上最難降的妖魔是什么向叉? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任墓陈,我火速辦了婚禮回官,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘剧防。我一直安慰自己京革,他們只是感情好销睁,可當(dāng)我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著存崖,像睡著了一般冻记。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上来惧,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天冗栗,我揣著相機與錄音,去河邊找鬼供搀。 笑死隅居,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的葛虐。 我是一名探鬼主播胎源,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼屿脐!你這毒婦竟也來了涕蚤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤的诵,失蹤者是張志新(化名)和其女友劉穎万栅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體西疤,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡烦粒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了代赁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扰她。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖芭碍,靈堂內(nèi)的尸體忽然破棺而出徒役,到底是詐尸還是另有隱情,我是刑警寧澤豁跑,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布廉涕,位于F島的核電站泻云,受9級特大地震影響艇拍,放射性物質(zhì)發(fā)生泄漏狐蜕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一卸夕、第九天 我趴在偏房一處隱蔽的房頂上張望层释。 院中可真熱鬧,春花似錦快集、人聲如沸贡羔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乖寒。三九已至,卻和暖如春院溺,著一層夾襖步出監(jiān)牢的瞬間楣嘁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工珍逸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留逐虚,地道東北人。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓谆膳,卻偏偏與公主長得像叭爱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子漱病,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,666評論 2 350

推薦閱讀更多精彩內(nèi)容