给定一个只包含数字的字符串,复原它并返回所有可能的 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(), 0, 0);
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行添加的数字。