代码随想录算法训练营第二十七天 | 39. 组合总和、40. 组合总和 II、131. 分割回文串

100 阅读2分钟

39. 组合总和

代码随想录视频讲解

代码随想录文章讲解

回溯

class Solution:
    def __init__(self):
        self.res = []
        self.path = []
        
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        self._backtracking(candidates, target, 0)
        return self.res
    
    def _backtracking(self, candidates, target, start_index):
        if sum(self.path) == target:
            self.res.append(self.path[:])
            return
        if target < sum(self.path):
            return
        
        for i in range(start_index, len(candidates)):
            self.path.append(candidates[i])
            # because we can use a number unlimitedly, so we do not increase the start_index
            self._backtracking(candidates, target, i)
            self.path.pop()

剪枝

  • 需要先排序,使得candidates[i]是当前path能取到的最小值
class Solution:
    def __init__(self):
        self.path = []
        self.paths = []
​
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        '''
        因为本题没有组合数量限制,所以只要元素总和大于target就算结束
        '''
        self.path.clear()
        self.paths.clear()
​
        # 为了剪枝需要提前进行排序
        candidates.sort()
        self.backtracking(candidates, target, 0, 0)
        return self.paths
​
    def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
        # Base Case
        if sum_ == target:
            self.paths.append(self.path[:]) # 因为是shallow copy,所以不能直接传入self.path
            return
        # 单层递归逻辑 
        # 如果本层 sum + condidates[i] > target,就提前结束遍历,剪枝
        for i in range(start_index, len(candidates)):
            if sum_ + candidates[i] > target: 
                return 
            sum_ += candidates[i]
            self.path.append(candidates[i])
            self.backtracking(candidates, target, sum_, i)  # 因为无限制重复选取,所以不是i-1
            sum_ -= candidates[i]   # 回溯
            self.path.pop()        # 回溯

40. 组合总和 II

代码随想录文章讲解

20201123202817973

回溯+剪枝

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        path = []
        # 必须提前进行数组排序,避免重复
        candidates.sort()
        sum_ = 0
        
        def _backtracking(start_index, candidates, target):
            nonlocal path
            nonlocal res
            nonlocal sum_
            
            if sum_ == target:
                res.append(path[:])
                return
            
            for i in range(start_index, len(candidates)):
                if sum_ + candidates[i] > target:
                    return
                # 跳过同一树层使用过的元素
                if i > start_index and candidates[i] == candidates[i-1]:
                    continue
                    
                sum_ += candidates[i]
                path.append(candidates[i])
                _backtracking(i+1, candidates, target)
                sum_ -= candidates[i]
                path.pop()
                
        _backtracking(0, candidates, target)
        return res

131. 分割回文串

代码随想录视频讲解

代码随想录文章讲解

回溯+函数判断回文串

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        res = []
        path = []
        
        def _backtracking(start_index, s):
            nonlocal path
            nonlocal res
            
            if start_index >= len(s):
                res.append(path[:])
                return
            
            for i in range(start_index, len(s)):
                # substring s[start : i+1], len = i - start_index + 1
                if self.is_palindrome(s, start_index, i):
                    path.append(s[start_index : i+1])
                    _backtracking(i+1, s)
                    path.pop()
                        
        _backtracking(0, s)
        return res
            
    
    def is_palindrome(self, s, start, end):
        l, r = start, end
        while l <= r:
            if s[l] != s[r]:
                return False
            l += 1
            r -= 1
        return True

判断回文串可以使用正反序判断

temp = s[start_index:i+1]
if temp == temp[::-1]: 
  self.path.append(temp)
  self.backtracking(s, i+1)   # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
  self.path.pop()