剑指 Offer II 087. 复原 IP(93. 复原 IP 地址)

题目:

 

 

思路:

【1】回溯的方式处理

代码展示:

进行优化:

//时间0 ms击败100%
//内存40.2 MB击败90.50%
//首先做了范围判断免去无用的递归,其次,再遇到前导零的时候进行剪枝,但这种其实提高不了多少
//其次能变为0ms的原因主要是将StringBuilder 替换了 StringBuffer,两者区别一个前者无锁,后者则是加了锁
//在多线程下其实应该选后者,但是在做题中应该先前者
class Solution {
    static final int SEG_COUNT = 4;
    List<String> ans = new ArrayList<String>();
    int[] segments = new int[SEG_COUNT];

    public List<String> restoreIpAddresses(String s) {
        if(s.length()<4||s.length()>12){
            return ans;
        }
        segments = new int[SEG_COUNT];
        dfs(s, 0, 0);
        return ans;
    }

    public void dfs(String s, int segId, int segStart) {
        // 如果找到了 4 段 IP 地址并且遍历完了字符串,那么就是一种答案
        if (segId == SEG_COUNT) {
            if (segStart == s.length()) {
                StringBuilder ipAddr = new StringBuilder();
                for (int i = 0; i < SEG_COUNT; ++i) {
                    ipAddr.append(segments[i]);
                    if (i != SEG_COUNT - 1) {
                        ipAddr.append('.');
                    }
                }
                ans.add(ipAddr.toString());
            }
            return;
        }

        // 如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯
        if (segStart == s.length()) return;

        // 由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0
        if (s.charAt(segStart) == '0') {
            segments[segId] = 0;
            dfs(s, segId + 1, segStart + 1);
            return;
        }

        // 一般情况,枚举每一种可能性并递归
        // 罗列当前段的可能性,只要字符串凑起来符合0~255都可能是当前段的值
        // 至于为什么上面的0要特殊的抽离出来,因为0000,转成数字的话也是0,实际应该是4段的
        int addr = 0;
        for (int segEnd = segStart; segEnd < s.length(); ++segEnd) {
            addr = addr * 10 + (s.charAt(segEnd) - '0');
            if (addr > 0 && addr <= 0xFF) {
                segments[segId] = addr;
                dfs(s, segId + 1, segEnd + 1);
            } else {
                break;
            }
        }
    }
}

 

 

 

回溯的方式:

//时间1 ms击败93.44%
//内存40.1 MB击败93.23%
//时间复杂度:O(3^SEG_COUNT * ∣s∣)。由于 IP 地址的每一段的位数不会超过 3,因此在递归的每一层,我们最多只会深入到下一层的 3 种情况。
//由于 SEG_COUNT=4,对应着递归的最大层数,所以递归本身的时间复杂度为 O(3^SEG_COUNT)。
//如果我们复原出了一种满足题目要求的 IP 地址,那么需要 O(∣s∣) 的时间将其加入答案数组中,因此总时间复杂度为 O(3^SEG_COUNT * ∣s∣)。
//空间复杂度:O(SEG_COUNT),这里只计入除了用来存储答案数组以外的额外空间复杂度。
//递归使用的空间与递归的最大深度 SEG_COUNT 成正比。
//并且在上面的代码中,我们只额外使用了长度为 SEG_COUNT 的数组 segments 存储已经搜索过的 IP 地址,因此空间复杂度为 O(SEG_COUNT)。

class Solution {
    static final int SEG_COUNT = 4;
    List<String> ans = new ArrayList<String>();
    int[] segments = new int[SEG_COUNT];

    public List<String> restoreIpAddresses(String s) {
        segments = new int[SEG_COUNT];
        dfs(s, 0, 0);
        return ans;
    }

    public void dfs(String s, int segId, int segStart) {
        // 如果找到了 4 段 IP 地址并且遍历完了字符串,那么就是一种答案
        if (segId == SEG_COUNT) {
            if (segStart == s.length()) {
                StringBuffer ipAddr = new StringBuffer();
                for (int i = 0; i < SEG_COUNT; ++i) {
                    ipAddr.append(segments[i]);
                    if (i != SEG_COUNT - 1) {
                        ipAddr.append('.');
                    }
                }
                ans.add(ipAddr.toString());
            }
            return;
        }

        // 如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯
        if (segStart == s.length()) return;

        // 由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0
        if (s.charAt(segStart) == '0') {
            segments[segId] = 0;
            dfs(s, segId + 1, segStart + 1);
        }

        // 一般情况,枚举每一种可能性并递归
        // 罗列当前段的可能性,只要字符串凑起来符合0~255都可能是当前段的值
        // 至于为什么上面的0要特殊的抽离出来,因为0000,转成数字的话也是0,实际应该是4段的
        int addr = 0;
        for (int segEnd = segStart; segEnd < s.length(); ++segEnd) {
            addr = addr * 10 + (s.charAt(segEnd) - '0');
            if (addr > 0 && addr <= 0xFF) {
                segments[segId] = addr;
                dfs(s, segId + 1, segEnd + 1);
            } else {
                break;
            }
        }
    }
}

 

posted @ 2023-04-11 12:23  忧愁的chafry  阅读(15)  评论(0编辑  收藏  举报