算法学习14—回溯 子集型 分割回文串

回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili

当题目中出现 “所有组合” 等类似字眼时,我们第一感觉就要想到用回溯。 

17. 电话号码的字母组合 

17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

写法一 

MAPPING = "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        n = len(digits)
        if n == 0:
            return []
        ans = []
        path = [''] * n
        def dfs(i: int) -> None:
            if i == n:
                ans.append(''.join(path))
                return
            for c in MAPPING[int(digits[i])]:
                path[i] = c
                dfs(i + 1)
        dfs(0)
        return ans

n是digits的长度,如果n为0,则直接返回空列表[]。

ans用于存储所有可能的字母组合,path是一个长度为n的列表,初始化为全空字符。

dfs是递归函数,它的参数i表示当前处理到digits中的第i个位置。 

在dfs函数中,首先判断如果i等于n,说明已经处理完了所有数字,此时将path中的字符组合成一个字符串并添加到答案列表ans中,然后返回。

接着,遍历MAPPING[int(digits[i])],即遍历当前数字所对应的字符集中的所有字符,依次将字符赋值给path[i],然后递归调用dfs函数,处理digits中的下一个数字,直到i等于n为止。

最后,将ans作为函数的返回结果。

写法二 (回溯)

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        if not digits: return []

        phone = {'2':['a','b','c'],
                 '3':['d','e','f'],
                 '4':['g','h','i'],
                 '5':['j','k','l'],
                 '6':['m','n','o'],
                 '7':['p','q','r','s'],
                 '8':['t','u','v'],
                 '9':['w','x','y','z']}
                
        def backtrack(conbination,nextdigit):
            if len(nextdigit) == 0:
                res.append(conbination)
            else:
                for letter in phone[nextdigit[0]]:
                    backtrack(conbination + letter,nextdigit[1:])

        res = []
        backtrack('',digits)
        return res

定义了一个backtrack函数,它有两个参数,分别是当前已经生成的字符串combination和还未处理的数字串nextdigit。回溯算法通常使用递归来实现,这里也不例外。如果nextdigit的长度为0,说明已经处理完所有数字,此时将combination添加到结果列表res中即可。否则,遍历phone[nextdigit[0]],即遍历当前数字所对应的字符集中的所有字符,依次将其添加到combination后面,并递归调用backtrack函数,传入新的combination和去掉第一个数字后的nextdigit。注意,在递归调用之后,需要将combination恢复到初始状态,以便进行下一轮选择。

最后,在letterCombinations函数中,初始化结果列表res为空列表。调用backtrack函数,传入空字符串和digits,开始执行回溯算法。最终返回结果列表res作为函数的返回值。

78. 子集 

78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

方法一:输入的视角(选或不选) 

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        ans = []
        path = []
        n = len(nums)
        def dfs(i: int) -> None:
            if i == n:
                ans.append(path.copy())  # 固定答案
                return
            # 不选 nums[i]
            dfs(i + 1)
            # 选 nums[i]
            path.append(nums[i])
            dfs(i + 1)
            path.pop()  # 恢复现场
        dfs(0)
        return ans

摘自评论—关于“恢复现场”的个人理解:
在递归到某一“叶子节点”(非最后一个叶子)时,答案需要向上返回,而此时还有其他的子树(与前述节点不在同一子树)未被递归到,又由于path为全局变量。若直接返回,会将本不属于该子树的答案带上去,故需要恢复现场。
恢复现场的方式为:在递归完成后 dfs(i+1); 后,进行与“当前操作”相反的操作,“反当前操作”。 

 方法二:答案的视角(选哪个数)

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        ans = []
        path = []
        n = len(nums)
        def dfs(i: int) -> None:
            ans.append(path.copy())  # 固定答案
            if i == n:
                return
            for j in range(i, n):  # 枚举选择的数字
                path.append(nums[j])
                dfs(j + 1)
                path.pop()  # 恢复现场
        dfs(0)
        return ans

131. 分割回文串 

131. 分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

回文串 是正着读和反着读都一样的字符串。

方法一:输入的视角(逗号选或不选)

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        ans = []
        path = []
        n = len(s)

        # start 表示当前这段回文子串的开始位置
        def dfs(i: int, start: int) -> None:
            if i == n:
                ans.append(path.copy())  # 固定答案
                return

            # 不选 i 和 i+1 之间的逗号(i=n-1 时右边没有逗号)
            if i < n - 1:
                dfs(i + 1, start)

            # 选 i 和 i+1 之间的逗号
            t = s[start: i + 1]
            if t == t[::-1]:  # 判断是否回文
                path.append(t)
                dfs(i + 1, i + 1)
                path.pop()  # 恢复现场

        dfs(0, 0)
        return ans

方法二:答案的视角(枚举子串结束位置)

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        ans = []
        path = []
        n = len(s)
        def dfs(i: int) -> None:
            if i == n:
                ans.append(path.copy())  # 固定答案
                return
            for j in range(i, n):  # 枚举子串的结束位置
                t = s[i: j + 1]
                if t == t[::-1]:  # 判断是否回文
                    path.append(t)
                    dfs(j + 1)
                    path.pop()  # 恢复现场
        dfs(0)
        return ans

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值