0、参考资料
一、 题目描述
给定一个只包含数字的字符串 s ,用以表示一个 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 地址。
输入:s = “25525511135”
输出:[“255.255.11.135”,“255.255.111.35”]
输入:s = “0000”
输出:[“0.0.0.0”]
二、 代码思路
摆明了就是回溯算法,选择元素的条件:前导元素不能是0,选择的子串必须0-255 且没有特殊符号。跟87题有点相似的感觉。
回溯算法本身作用就是选择元素,并根据所有选择的情况,形成一棵选择的的回溯搜索树,该树的每一条路径都是一种选择结果。
所以,我们来分析题目,在给定字符串中让我们选可能的IP地址组合,我们知道,IP地址其实就是由四块组成,说白了,就是让我们找不同的子串组合,这些不同的子串分别组合不同的四块,从而形成不同的IP地址。
那我们选择子串的时候,就能够很好的利用回溯算法,选择这个子串作为IP地址之一,看看形成的IP可行吗,然后如果我们不选这个子串作为IP(回溯),看看可行吗,这就是基于回溯的动态选择的特性,每一步都能判断,从而形成最终的结果集合。
语言描述总是不够,建议结合代码理解,结合上一道题找回文子串,就能发现这两题的回溯思想其实一摸一样,只不过选择元素的条件变了,前者是我选择的元素必须是回文串 ,后者的选择条件是我选择的子串必须是合理的IP(0 - 255,单个字符可为0,多个字符首位不能为0)。
基本上就是以这段代码为模板:
//回溯算法
public void dfs(int cur) {
if (cur == n) {
res.add(new ArrayList(path));
return;
}
for (int i = cur; i < n; i++) {
if (dp[cur][i] == 1) {
path.add(s.substring(cur, i + 1));
//注意,这里的回溯是选择回文子串,而并非是选择单个字符。
//所以,这里并不能是 cur + 1,而应该是i + 1.
//原因就是:cur - i 已经成为一个子串了,那么证明该字串被选。
//那么我们就应该从i + 1,开始再去选择下一个子串。
//这个思想很重要。
dfs(i + 1);
path.remove(path.size() - 1);
}
}
}
三、 代码题解
class Solution {
private List<String> list = new ArrayList();
private List<List<String>> resList = new ArrayList();
private String s;
public List<String> restoreIpAddresses(String s) {
//摆明了就是回溯算法,选择元素的条件:前导元素不能是0,选择的子串必须0-255 且没有特殊符号
//跟86题有点相似的感觉
//先进行过滤操作,如果有负数和特殊字符就删掉,但是题目要求字符串s仅由数字组成,那么不可能有负数 (-1 -不是数字)
if (s.length() > 12) {
return new ArrayList();
}
this.s = s;
dfs(0);
List<String> res = new ArrayList();
//生成结果集,就是转换一下格式没啥用
for (int i = 0; i < resList.size(); i++) {
StringBuilder sb = new StringBuilder();
List<String> temp = resList.get(i);
for (int j = 0; j < temp.size(); j++) {
sb.append(temp.get(j));
}
sb.deleteCharAt(sb.length() - 1);
res.add(new String(sb));
}
return res;
}
private void dfs(int pos) {
if (pos == s.length() && list.size() == 4) {
resList.add(new ArrayList(list));
return;
}
//其实只需要遍历三个即可
for (int i = pos; i < s.length(); i++) {
//pos - i 这段字符串前导为0,但是该字符串长度不是1即并不是单个字符为0
if (i - pos >= 1 && s.charAt(pos) == '0') {
break;
}
//当子串长度超过 0 - 255时候也不选
if (Integer.parseInt(s.substring(pos, i + 1)) > 255) {
break;
}
list.add(s.substring(pos, i + 1) + ".");
dfs(i + 1);
list.remove(list.size() - 1);
}
}
}
//1.