代码随想录算法训练营第二十三天|LC39.组合总和|LC40.组合总和Ⅱ|LC131.分割回文串

39. 组合总和 - 力扣(LeetCode)

        题目要求:可以无限制重复被选取;如下图所示:

 本题仅有总和的限制,所以没有递归层数的限制,只要选取的元素总和超过target就返回;

from typing import List

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        self.backtracking(candidates, target, 0, 0, [], res)
        return res

    def backtracking(self, candidates, target, total, startIndex, path, res):
        # 当累加值大于目标值,返回
        if total > target:
            return
        if total == target:
            res.append(path[:])
            return

        for i in range(startIndex, len(candidates)):
            total += candidates[i]
            path.append(candidates[i])
            self.backtracking(candidates, target, total, i, path, res)
            # 回溯,小于目标值,返回上一层,便于进行下一层的另一个元素进行累加
            total -= candidates[i]
            # 暂时数据存储中弹出该值
            path.pop()

if __name__ == '__main__':
    candidates = [2,3,6,7]
    target = 7
    res = Solution().combinationSum(candidates, target)
    print(res)

        在看到上图遍历的过程时,就已经想到,为什么要一遍遍判断累加值大于目标值的情况,若一个数值是有序的,若累加到第一个值就大于目标值的话,数组的后序数值就不需要进行累加,直接就可以return,这样不是更方便吗?

        下部分代码就是对该部分进行调整后的代码(剪枝):

from typing import List

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        # 对数组进行排序,若累加值大于第一个值时就可以直接return,不需要对后面的值进行计算
        candidates.sort()
        self.backtracking(candidates, target, 0, 0, [], res)
        return res

    def backtracking(self, candidates, target, total, startIndex, path, res):
        # 当累加值大于目标值,返回
        if total == target:
            res.append(path[:])
            return

        for i in range(startIndex, len(candidates)):
            # 对累加值进行判断,若大于目标值,直接break,跳出本次循环
            if total + candidates[i] > target:
                break
            total += candidates[i]
            path.append(candidates[i])
            self.backtracking(candidates, target, total, i, path, res)
            # 回溯,小于目标值,返回上一层,便于进行下一层的另一个元素进行累加
            total -= candidates[i]
            # 暂时数据存储中弹出该值
            path.pop()

if __name__ == '__main__':
    candidates = [2,3,6,7]
    target = 7
    res = Solution().combinationSum(candidates, target)
    print(res)

40. 组合总和 II - 力扣(LeetCode)

        本题要求candidates 中的每个数字在每个组合中只能使用一次 ,我理解为使用该数字后,需要把该数字在candidates中删除,然后再剩余的数字中进行遍历;该思路就是去重的思路,便于后序递归对该值进行重复遍历;那需要一种什么方法来进行去重操作呢?(所谓去重,其实就是使用过的元素不能重复选取,去重的话,要进行数组的排序,便于判断前后值是否是同一个值

from typing import List

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        candidates.sort()
        self.backtracking(candidates, target, 0, 0, [], res)
        return res

    def backtracking(self, candidates, target, total, startIndex, path, res):
        if total == target:
            res.append(path[:])
            return
        for i in range(startIndex, len(candidates)):
            # 要对同一树层使用过的元素进行跳过
            if i > startIndex and candidates[i] == candidates[i-1]:
                continue
            if total + candidates[i] > target:
                break
            total += candidates[i]
            path.append(candidates[i])
            # 这里是i+1,每个数字在每个组合中只能使用一次(未完全理解)
            self.backtracking(candidates, target, total, i+1, path, res)
            # 回溯
            total -= candidates[i]
            path.pop()

if __name__ == '__main__':
    candidates = [10,1,2,7,6,1,5]
    target = 8
    res = Solution().combinationSum2(candidates, target)
    print(res)

        集合去重的重任就是used来完成的,一个bool型数组,用来记录同一树枝上的元素是否使用过;(该方法还未理解)

from typing import List

# 使用used进行判断去重
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        used = [False] * len(candidates)
        res = []
        candidates.sort()
        self.backtracking(candidates, target, 0, 0, used, [], res)
        return res

    def backtracking(self, candidates, target, total, startIndex, used, path, res):
        if total == target:
            res.append(path[:])
            return
        for i in range(startIndex, len(candidates)):
            # 对于向太阳的数字,只选择第一个未被使用的数字,跳过其他相同数字
            if i > startIndex and candidates[i] == candidates[i-1] and not used[i-1]:
                continue
            if total + candidates[i] > target:
                break
            total += candidates[i]
            path.append(candidates[i])
            used[i] = True
            # 这里是i+1,每个数字在每个组合中只能使用一次(未完全理解)
            self.backtracking(candidates, target, total, i+1, used, path, res)
            # 回溯
            used[i] = False
            # 回溯
            total -= candidates[i]
            path.pop()

if __name__ == '__main__':
    candidates = [10,1,2,7,6,1,5]
    target = 8
    res = Solution().combinationSum2(candidates, target)
    print(res)

131. 分割回文串 - 力扣(LeetCode)

         首先明白回文串的定义:“回文串”是一个正读和反读都一样的字符串;

        切割问题和上一题一个意思,即不能再原来切割过的地方进行重新切割,即要选取剩下的部分进行切割,类似于组合问题:

例如对于字符串abcdef:

  • 组合问题:选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中再选取第三个.....。
  • 切割问题:切割一个a之后,在bcdef中再去切割第二段,切割b之后在cdef中再切割第三段.....。

from typing import List


class Solution:
    def partition(self, s: str) -> List[List[str]]:
        res = []
        self.backtracking(s, 0, [], res)
        return res

    def backtracking(self, s, start_index, path, res):
        if start_index == len(s):
            res.append(path[:])
            return

        for i in range(start_index, len(s)):
            # 判断被截取的字符串([start_index, i])是否是回文串;
            if self.is_palindrome(s, start_index, i):
                path.append(s[start_index:i+1])
                self.backtracking(s, i+1, path, res)
                path.pop()

    def is_palindrome(self,s:str, start:int, end:int) -> bool:
        i = start
        j = end
        while i < j:
            if s[i] != s[j]:
                return False
            i += 1
            j -= 1
        return True

if __name__ == '__main__':
    s = "aab"
    res = Solution().partition(s)
    print(res)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值