回溯题目:复原 IP 地址

文章目录

题目

标题和出处

标题:复原 IP 地址

出处:93. 复原 IP 地址

难度

5 级

题目描述

要求

有效 IP 地址 由恰好四个整数和分隔整数的点组成。每个整数在范围 0 \texttt{0} 0 到 255 \texttt{255} 255 中,且不能含有前导零。

  • 例如: "0.1.2.201" \texttt{"0.1.2.201"} "0.1.2.201" 和 "192.168.1.1" \texttt{"192.168.1.1"} "192.168.1.1" 是有效 IP 地址,但是 "0.011.255.245" \texttt{"0.011.255.245"} "0.011.255.245"、 "192.168.1.312" \texttt{"192.168.1.312"} "192.168.1.312" 和 "192.168@1.1" \texttt{"192.168@1.1"} "192.168@1.1" 是无效 IP 地址。

给定一个只包含数字的字符串 s \texttt{s} s,返回通过在 s \texttt{s} s 中插入点可以得到的所有可能的有效 IP 地址。不能 重新排序或删除 s \texttt{s} s 中的任何数字。可以按任意顺序返回答案。

示例

示例 1:

输入: s = "25525511135" \texttt{s = "25525511135"} s = "25525511135"

输出: "255.255.11.135","255.255.111.35" \texttt{"255.255.11.135","255.255.111.35"} "255.255.11.135","255.255.111.35"

示例 2:

输入: s = "0000" \texttt{s = "0000"} s = "0000"

输出: "0.0.0.0" \texttt{"0.0.0.0"} "0.0.0.0"

示例 3:

输入: s = "101023" \texttt{s = "101023"} s = "101023"

输出: "1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3" \texttt{"1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"} "1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"

数据范围

  • 1 ≤ s.length ≤ 20 \texttt{1} \le \texttt{s.length} \le \texttt{20} 1≤s.length≤20
  • s \texttt{s} s 仅由数字组成

解法

思路和算法

由于有效 IP 地址由 4 4 4 个整数组成,因此需要在字符串 s s s 中插入 3 3 3 个点得到有效的 IP 地址。可以使用回溯得到所有可能的有效 IP 地址。

回溯过程中需要记录当前的起始下标 start \textit{start} start 和片段数 segmentCount \textit{segmentCount} segmentCount,使用长度为 4 4 4 的数组记录每个片段对应的整数,初始时 start = 0 \textit{start} = 0 start=0, segmentCount = 0 \textit{segmentCount} = 0 segmentCount=0。对于每个片段,只有当前片段不含前导零且对应的整数在范围 0 , 255 0, 255 0,255 中,才可能是有效 IP 地址,此时继续遍历后面的片段。具体做法如下。

  • 如果 segmentCount = 4 \textit{segmentCount} = 4 segmentCount=4,则只有当字符串 s s s 遍历结束时才能得到有效的 IP 地址。当 start = n \textit{start} = n start=n 时,字符串 s s s 遍历结束,根据数组中的整数生成 IP 地址,添加到答案中。

  • 如果 segmentCount < 4 \textit{segmentCount} < 4 segmentCount<4,则当前片段对应的片段编号为 segmentCount \textit{segmentCount} segmentCount。用 end \textit{end} end 表示当前片段的结束下标,用 s start : end s\\textit{start} : \\textit{end} sstart:end 表示字符串 s s s 的下标范围 start , end \\textit{start}, \\textit{end} start,end 的子数组,将 end \textit{end} end 从 start \textit{start} start 到 n − 1 n - 1 n−1 依次遍历,执行如下操作。

    • 如果 s start : end s\\textit{start} : \\textit{end} sstart:end 不含前导零且对应的整数在范围 0 , 255 0, 255 0,255 中,则当前片段可能是有效 IP 地址的一个片段,将当前片段对应的整数填入数组的下标 segmentCount \textit{segmentCount} segmentCount 处,继续对下一个片段回溯,下一个片段的起始下标是 end + 1 \textit{end} + 1 end+1,下一个片段的片段数是 segmentCount + 1 \textit{segmentCount} + 1 segmentCount+1。

    • 如果 s start : end s\\textit{start} : \\textit{end} sstart:end 含前导零或对应的整数不在范围 0 , 255 0, 255 0,255 中,则当前片段不可能是有效 IP 地址的一个片段,如果继续将 end \textit{end} end 向右移动则当前片段一定不符合有效 IP 地址的规则,因此结束对当前 start \textit{start} start 的回溯。

回溯结束时,即可得到所有可能的有效 IP 地址。

实现方面,由于有效 IP 地址的每个片段的长度都在范围 1 , 3 1, 3 1,3 中,因此有效 IP 地址的数字位数(不包括点)应该在范围 4 , 12 4, 12 4,12 中。如果 s s s 的长度小于 4 4 4 或大于 12 12 12,则一定不存在有效 IP 地址,返回空列表。只有当 s s s 的长度在范围 4 , 12 4, 12 4,12 中时才需要使用回溯得到所有可能的有效 IP 地址。

代码

java 复制代码
class Solution {
    static final int SEGMENTS = 4;
    List<String> ipAddresses = new ArrayList<String>();
    int[] ipArr = new int[SEGMENTS];
    int n;
    String s;

    public List<String> restoreIpAddresses(String s) {
        this.n = s.length();
        this.s = s;
        if (n < SEGMENTS || n > 3 * SEGMENTS) {
            return ipAddresses;
        }
        backtrack(0, 0);
        return ipAddresses;
    }

    public void backtrack(int start, int segmentCount) {
        if (segmentCount == SEGMENTS) {
            if (start == n) {
                ipAddresses.add(convert(ipArr));
            }
        } else {
            boolean flag = true;
            for (int end = start; end < n && flag; end++) {
                if (valid(start, end)) {
                    ipArr[segmentCount] = Integer.parseInt(s.substring(start, end + 1));
                    backtrack(end + 1, segmentCount + 1);
                } else {
                    flag = false;
                }
            }
        }
    }

    public boolean valid(int start, int end) {
        int length = end - start + 1;
        if (length < 1 || length > 3) {
            return false;
        }
        if (length > 1 && s.charAt(start) == '0') {
            return false;
        }
        return Integer.parseInt(s.substring(start, end + 1)) <= 255;
    }

    public String convert(int[] ipArr) {
        StringBuffer sb = new StringBuffer();
        sb.append(ipArr[0]);
        for (int i = 1; i < SEGMENTS; i++) {
            sb.append('.');
            sb.append(ipArr[i]);
        }
        return sb.toString();
    }
}

复杂度分析

  • 时间复杂度: O ( n 4 ) O(n^4) O(n4),其中 n n n 是字符串 s s s 的长度。由于有效 IP 地址有 4 4 4 个片段,因此需要插入 3 3 3 个点,插入点的方案数是 O ( n 3 ) O(n^3) O(n3),每种方案需要 O ( n ) O(n) O(n) 的时间得到 IP 地址和将有效 IP 地址添加到答案中,因此时间复杂度是 O ( n 4 ) O(n^4) O(n4)。

  • 空间复杂度: O ( C ) O(C) O(C),其中 C C C 是有效 IP 地址的片段数, C = 4 C = 4 C=4。需要创建长度为 C C C 的数组记录每个片段对应的整数,递归调用栈的层数是 O ( C ) O(C) O(C)。注意返回值不计入空间复杂度。

相关推荐
伟大的车尔尼18 天前
回溯题目:电话号码的字母组合
回溯
z200509301 个月前
今日算法(带回文问题的回溯)
算法·leetcode·回溯
每天回答3个问题2 个月前
LeetCodeHot100|回溯算法、46.全排列、78.子集、17.电话号码的字母组合
算法·深度优先·回溯
superior tigre2 个月前
78 子集
算法·leetcode·深度优先·回溯
kronos.荒2 个月前
非递减序列(python)
python·回溯
kronos.荒2 个月前
回溯(python)
python·回溯
kronos.荒3 个月前
N皇后问题(python)
python·回溯
kronos.荒3 个月前
全排列2(重复元素去重、python)
python·回溯
W23035765733 个月前
0-1 背包进阶:回溯法(子集树)+ 分支限界优化 极致详解(C++ 完整实现)
动态规划·回溯·算法设计·0-1背包