(回溯法)Java 求解复原 IP 地址

文章目录

一、题目

给定一个只包含数字的字符串,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 IP 地址 。你可以按任何顺序返回答案。

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

例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效 IP 地址。

(回溯法)Java 求解复原 IP 地址

二、回溯分析

该题和 分割回文串 类似,也可以看作是组合问题,然后判定每个子字符串是否符合要求

(1)确定递归参数

因为符合条件的子字符串需要给其加 ".",回溯时,也需要删除这个 ".",所以采用 StringBuilder 来操作
需要的参数有,字符串s,切割的起始下标 startIndex(防止重复),记录添加逗点的pointNum

(2)递归终止条件

pointNum==3时,表示IP地址组成,此时需要判断第四段也就是(startIndex,sb.length()-1)是否符合要求,符合则进行记录

//逗号数量为3时,结束分隔
if (pointNum == 3) {
    //判断第四段子字符串是否合法
    if (isValid(sb.toString(), startIndex, sb.length() - 1)) {
        list.add(sb.toString());
    }
}

(3)确定回溯搜索过程

startIndex 表示下一轮递归遍历的起始位置,也可以看作切割线
for (int i = startIndex; i < s.length(); i++)循环中,我们 定义了起始位置startIndex,那么 [startIndex, i] 就是要截取的子串
判断这个子串是否符合要求

判断是否符合要求:

IP段为0开头的数字不合法
IP段有非正整数字符不合法
IP段大于255不合法

//判断子字符串是否合法
private boolean isValid(String s, int start, int end) {
    if (start > end) {
        return false;
    }
    //0开头的数字不合法
    if (s.charAt(start) == '0' && start != end) {
        return false;
    }
    int num = 0;
    for (int i = start; i <= end; i++) {
        if (s.charAt(i) > '9' || s.charAt(i) < '0') {
            return false;
        }
        num = num * 10 + s.charAt(i) - '0';
        if (num > 255) {
            return false;
        }
    }
    return true;
}

三、代码

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

    public List<String> restoreIpAddresses(String s) {
        //长度不够,或者超出范围直接返回
        if (s.length() < 4 || s.length() > 12) {
            return list;
        }
        //方便操作,将 String 转为 StringBuilder
        sb.append(s);
        backTracking(sb, 0, 0);
        return list;
    }

    private void backTracking(StringBuilder sb, int startIndex, int pointNum) {
        //逗号数量为3时,结束分隔
        if (pointNum == 3) {
            //判断第四段子字符串是否合法
            if (isValid(sb.toString(), startIndex, sb.length() - 1)) {
                list.add(sb.toString());
            }
        }
        for (int i = startIndex; i < sb.length(); i++) {
            if (isValid(sb.toString(), startIndex, i)) {
                //在i的后面插入一个逗点
                sb.insert(i + 1, ".");
                //插入逗点后,下一个子串的起始位置为i+2
                backTracking(sb, i + 2, ++pointNum);
                //回溯删掉逗点
                pointNum--;
                sb.deleteCharAt(i + 1);
            } else {
                break;
            }
        }
    }
    //判断子字符串是否合法
    private boolean isValid(String s, int start, int end) {
        if (start > end) {
            return false;
        }
        //0开头的数字不合法
        if (s.charAt(start) == '0' && start != end) {
            return false;
        }
        int num = 0;
        for (int i = start; i <= end; i++) {
            if (s.charAt(i) > '9' || s.charAt(i) < '0') {
                return false;
            }
            num = num * 10 + s.charAt(i) - '0';
            if (num > 255) {
                return false;
            }
        }
        return true;
    }
}

四、总结

同切割问题,类似组合,需要对每一种组合进行判断是否符合要求

上一篇:명품 C++ 문자열 처리 응용 - 덧셈 문자열을 입력 받아 덧셈 실행


下一篇:Jenkins +git +python 进行持续集成进行接口测试(接口测试jenkins持续集成篇)