package BDyNamicProgramming;
import java.util.ArrayList;
import java.util.List;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/4/26 0026 17:34
* 复原IP地址
*/
public class Problem93 {
/**
* 回溯+剪枝
* @param s
* @return
* 回溯算法事实上就是在一个树形问题上做深度优先遍历,因此首先需要把问题转换成树形问题
* 这里请及大家一定要拿起纸盒笔,模拟一下如何通过制定的字符串s生成IP地址的过程,把树形图画出来
* 在画树形图的过程中,一定会发现有些树枝是没有必要的,把没有必要的枝叶剪去的操作就是兼职
这个问题思想不难,但是细节比较繁琐,什么时候递归终止,如何手动截取字符串,再转换成int类型
还有如何在中间节点发现可以兼职,这些
*/
/**
* 本题可以使用回溯和递归的思想复原ip地址:
*
* 1、首先创建ans来接受复原后的所有ip地址,然后通过创建 回溯方法进行筛选,最终返回ans
*
* 2、创建回溯方法体需要传入四各参数进行把控:
* 1、给定的数字字符串s
* 2、回溯过程中遍历到的位置pos,
* 3、当前确定好的ip段的数量
* 4、收集结果的ans
*
* 3、考虑方法体出口:如果确定好4个段并且遍历完整个s,就将cur之间的端以.分隔开发来放入ans
*
* 4、接下来对s进行筛选,其中注意每段的长度最大为3,拆箱int后的长度不超过,其实位置不能为0
*
* @param s
* @return
*/
public List<String> restoreIpAddresses(String s) {
List<String> ans = new ArrayList<>();
if (s == null || s.length() == 0) {
return ans;
}
// 回溯
back(s, 0, new ArrayList<>(), ans);
return ans;
}
// 中间两个参数解释:pos-当前遍历到 s 字符串中的位置,cur-当前存放已经确定好的 ip 段的数量
/**
*
* @param s
* @param pos 当前遍历到 s 字符串中的位置
* @param cur cur-当前存放已经确定好的 ip 段的数量
* @param ans 最终的结果
*/
private void back(String s, int pos, List<String> cur, List<String> ans) {
//递归结束条件
//当前遍历的cur中的个数为4
if (cur.size() == 4) {
// 如果此时 pos 也刚好遍历完整个 s
if (pos == s.length()) {
// join 用法:例如 [[255],[255],[111],[35]] -> 255.255.111.35
ans.add(String.join(".", cur));
}
return;
}
// ip 地址每段最多有三个数字
for (int i = 1; i <= 3; i++) {
// 如果当前位置距离 s 末尾小于 3 就不用再分段了,直接跳出循环即可。
if (pos + i > s.length()) {
break;
}
// 将 s 的子串开始分段:pos 到pos i
String segment = s.substring(pos, pos + i);
// 剪枝条件:段的起始位置不能为 0,
// 段拆箱成 int 类型的长度不能大于 255
if ((segment.startsWith("0") && segment.length() > 1)|| (i == 3 && Integer.parseInt(segment) > 255)) {
continue;
}
// 符合要求就加入到 cur 数组中
cur.add(segment);
// 继续递归遍历下一个位置
back(s, pos + i, cur, ans);
// 回退到上一个元素,即回溯
cur.remove(cur.size() - 1);
}
}
}