leetcode 93. 复原 IP 地址

题目描述

有效 IP 地址 正好由四个整数(每个整数位于 0255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

  • 例如:"0.1.2.201" "192.168.1.1"有效 IP 地址,但是 "0.011.255.245""192.168.1.312""192.168@1.1"无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

示例

输入:s = "101023"
输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]

题目链接

https://leetcode.cn/problems/restore-ip-addresses/

解题思路

核心是将三个 .放在哪儿?

  • 不能有前导 0
  • 整数位于 0255之间

pointNum记录已插入 .的数量,index记录当前截断字符串的起始下标。那么只有当 index == s.length()pointNum == 4同时满足时,才是将整个字符串分割完成,即一次有效分割,添加结果到结果集合。其余情况均为无效分割。

同时只有当 pointNum < 3时,才在当前路径后添加 .

剪枝情况:

  • i - index < 3每一段 ip至多不超过 3个字符
  • i > index && s.charAt(index) == '0'去除前导 0
  • Integer.parseInt(s.substring(index, i + 1)) > 255去除大于 255的情况,由于题目明确 s只包含数字,所以满足上述情况,其数值一定大于 0,不用再做这部分剪枝

另一个关键点,使用 lenBefore记录当前情况的长度,之后使用 setLength(lenBefore)进行回溯删除。

class Solution {
    List<String> ans = new ArrayList<>();
    StringBuilder sb = new StringBuilder();

    public List<String> restoreIpAddresses(String s) {
        traceBack(s, 0, 0);
        return ans;
    }

    public void traceBack(String s, int index, int pointNum) {
        if (index == s.length() && pointNum == 4) {
            ans.add(sb.toString());
            return;
        }
        if (index == s.length() || pointNum == 4) return;

        for (int i = index; i < s.length() && i - index < 3; i++) {
            if (s.charAt(index) == '0' && i > index) break;
            int num = Integer.parseInt(s.substring(index, i + 1));
            if (num > 255) break;

            int lenBefore = sb.length();  // 记录长度
            sb.append(s, index, i + 1);
            if (pointNum < 3) sb.append('.');
            traceBack(s, i + 1, pointNum + 1);
            sb.setLength(lenBefore);  // 回溯删除
        }
    }
}

leetcode 78. 子集

题目描述

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

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

示例

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

题目链接

https://leetcode.cn/problems/subsets

解题思路

在每次访问中记录当前的访问情况。终止状态可以不用写。

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        traceBack(nums, 0);
        return ans;
    }
    public void traceBack(int[] nums, int index) {
        ans.add(new ArrayList<>(path));
        /*
        if (index == nums.length) {
            return;
      
        }
        */
        for (int i = index; i < nums.length; i++) {
            path.add(nums[i]);
            traceBack(nums, i + 1);
            path.removeLast();
        }
    }
}

leetcode 90. 子集 II

题目描述

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的 子集(幂集)。

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

示例

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

题目链接

https://leetcode.cn/problems/subsets-ii

解题思路

和上一题一样,不同的是,需要先对数组排序,然后使用 i > index && nums[i] == nums[i - 1]剪枝

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        traceBack(nums, 0);
        return ans;
    }
    public void traceBack(int[] nums, int index) {
        ans.add(new ArrayList<>(path));
        if (index == nums.length) {
            return;
        }
        for (int i = index; i < nums.length; i++) {
            // i > index, 不是i > 0。只有用过了,才能筛,而不是从头开始
            if (i > index && nums[i] == nums[i - 1]){
                continue;
            }
            path.add(nums[i]);
            traceBack(nums, i + 1);
            path.removeLast();
        }
    }
}