给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

示例:

输入: "25525511135"

输出: ["255.255.11.135", "255.255.111.35"]

答案:

 1public List<String> restoreIpAddresses1(String s) {
2    List<String> res = new ArrayList<String>();
3    int len = s.length();
4    for (int i = 1; i < 4 && i < len - 2; i++) {
5        for (int j = i + 1; j < i + 4 && j < len - 1; j++) {
6            for (int k = j + 1; k < j + 4 && k < len; k++) {
7                String s1 = s.substring(0, i), s2 = s.substring(i, j), s3 = s.substring(j, k), s4 = s.substring(k, len);
8                if (isValid(s1) && isValid(s2) && isValid(s3) && isValid(s4)) {
9                    res.add(s1 + "." + s2 + "." + s3 + "." + s4);
10                }
11            }
12        }
13    }
14    return res;
15}
16
17public boolean isValid(String s) {
18    if (s.length() > 3 || s.length() == 0 || (s.charAt(0) == '0' && s.length() > 1) || Integer.parseInt(s) > 255)
19        return false;
20    return true;
21}

解法:

这种方式是最容易理解的,也是效率最低。再来看一种递归的解法

 1public List<String> restoreIpAddresses2(String s) {
2    List<String> result = new ArrayList<>();
3    doRestore(result, "", s, 0);
4    return result;
5}
6
7private void doRestore(List<String> result, String path, String s, int k) {
8    if (s.isEmpty() || k == 4) {
9        if (s.isEmpty() && k == 4)
10            result.add(path.substring(1));
11        return;
12    }
13    for (int i = 1; i <= (s.charAt(0) == '0' ? 1 : 3) && i <= s.length(); i++) {
14        String part = s.substring(0, i);
15        if (Integer.valueOf(part) <= 255)
16            doRestore(result, path + "." + part, s.substring(i), k + 1);
17    }
18}

这种解法不太好理解的for循环语句的循环条件,当字符串s的首字母不是0的时候,分别取他的第一位,第二位,第三位(假如s的长度大于等于3)进行递归运算,如果是0,他就不能和后面的任何数字结合,比如"255.255.1.135"是正确的ip地址,但"255.255.01.135"不是。上面的第10行会截取path的第一个字符是".",收益需要把它去掉。再来看最后一种解法

 1public List<String> restoreIpAddresses3(String s) {
2    List<String> list = new LinkedList<>();
3    backtrack(s, list, new StringBuilder(), 00);
4    return list;
5}
6
7private void backtrack(String s, List<String> list, StringBuilder sb, int index, int level) {
8    if (index > s.length() || level > 4)
9        return;
10    else if (index == s.length() && level == 4) {
11        list.add(sb.toString());
12        return;
13    }
14    for (int i = 1; i <= 3; i++) {
15        if (index + i > s.length()) break;
16        int num = Integer.valueOf(s.substring(index, index + i));
17        if (i == 1 || i == 2 && num >= 10 && num <= 99 || i == 3 && num >= 100 && num <= 255) {
18            sb.append(num);
19            if (level < 3)
20                sb.append(".");
21            backtrack(s, list, sb, index + i, level + 1);
22            if (level < 3)
23                sb.deleteCharAt(sb.length() - 1);
24            sb.delete(sb.length() - i, sb.length());
25        }
26    }
27}

这种是递归加回溯的算法,如果理解前面讲到的248,子集 II,那么理解这种解法就比较简单了。这里的回溯如果满足条件可能要回溯两步,第一步在23行删除20行添加的原点,第二部在24行删除18行添加的数字。