代码随想录算法训练营Day28 | 93. 复原IP地址 | 78. 子集 | 90. 子集II

93. 复原IP地址

题目链接 | 解题思路

题目的背景设置很复杂,但实际上是个分割问题。start_idx 是必不可少的,本题中还要额外记录分割的次数(可以从 curr_record 中得到)。

  • 参数和返回值:两个经典的全局变量;一个经典的 start_idx
  • 终止条件:题目要求 IP 的格式必须是四段,所以当 curr_record 中已经记录了 4 个分段的时候就该判断终止 – 如果此时的切割位置正好是字符串结尾,则是合法的结果,记录;否则就是无效结果,直接返回 None
  • 单层搜索逻辑:对于给定的 start_idx,只需要搜索其后面最多三位的分割位置,因为合法的分割最多只能给出长度为 3 的子字符串。注意,有时剩余字符串长度不足 3,所以要取 min(start_idx + 3, len(s))
class Solution:
    def __init__(self):
        self.curr_record = []
        self.results = []
    
    def backtrack(self, s: str, start_idx: int):
        if len(self.curr_record) > 4:
            return None
        
        if len(self.curr_record) == 4 and start_idx == len(s):
            self.results.append(".".join(self.curr_record))
            return None

        end_idx = min(start_idx + 3, len(s))
        for i in range(start_idx, end_idx):
            curr_s = s[start_idx: i + 1]
            if i == start_idx or curr_s[0] != '0':      # otherwise, leading 0 -> invalid
                if int(curr_s) <= 255:                  # otherwise, out of range -> invalid
                    self.curr_record.append(curr_s)
                    self.backtrack(s, i + 1)     
                    self.curr_record.pop()       

    def restoreIpAddresses(self, s: str) -> List[str]:
        self.backtrack(s, 0)
        return self.results

78. 子集

题目链接 | 解题思路

在之前的组合、分割问题中,都是收集树的叶子节点。子集问题则是需要收集树的每一个节点。

和之前的回溯问题一样,都需要 start_idx。但子集问题的终止条件有所区别,本题并不需要 explicit 的终止条件,当 start_idx >= len(nums) 时,for loop 就不会执行,相当于自动结束了递归。

子集问题没办法剪枝,因为从定义上就必须遍历所有的节点。

class Solution:
    def __init__(self):
        self.curr_record = []
        self.results = [[]]
    
    def backtrack(self, nums: List[int], start_idx: int):
        if start_idx == len(nums):
            return None
        
        for i in range(start_idx, len(nums)):
            self.curr_record.append(nums[i])
            self.results.append(self.curr_record.copy())
            self.backtrack(nums, i + 1)
            self.curr_record.pop()

    def subsets(self, nums: List[int]) -> List[List[int]]:
        self.backtrack(nums, 0)
        return self.results

90. 子集II

题目链接 | 解题思路

本题需要做去重,实际上和之前 组合总和II 以及之后排列去重的方法是一样的。由于这里是子集,所以也可以依靠 start_idx 进行去重,不需要使用 used 数组。
这里的做法是,对于第一次出现的元素值,照常进行递归+回溯;此后,对于有相同值的元素直接跳过,因为这些元素只会创造出已经记录过的子集。

注意,去重之前要先将输入的数组排序。

class Solution:
    def __init__(self):
        self.curr_record = []
        self.results = [[]]
    
    def backtrack(self, nums: List[int], start_idx: int):
        if start_idx == len(nums):
            return None
        
        curr_idx = start_idx
        while (curr_idx < len(nums)):
            self.curr_record.append(nums[curr_idx])
            self.results.append(self.curr_record.copy())
            self.backtrack(nums, curr_idx + 1)
            self.curr_record.pop()

            curr_idx += 1
            while (curr_idx < len(nums) and nums[curr_idx] == nums[curr_idx - 1]):
                curr_idx += 1

    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        self.backtrack(nums, 0)
        return self.results
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值