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);
}
}
}