简介

切割问题和组合问题相差不大,切割问题可以巧妙的转化为组合问题,套用组合问题的模板和剪枝技巧。

131.分割回文串

题目描述

image.png

解题思路

  • 递归函数参数

全局变量数组path存放切割后回文的子串,二维数组result存放结果集。 (这两个参数可以放到函数参数里)
本题递归函数参数还需要startIndex,因为切割过的地方,不能重复切割,和组合问题也是保持一致的。

  • 递归函数终止条件

党startIndex到达字符串的最后时,停止切割。

  • 单层搜索的逻辑

来看看在递归循环,中如何截取子串呢?
在for (int i = startIndex; i < s.size(); i++)循环中,我们 定义了起始位置startIndex,那么 [startIndex, i] 就是要截取的子串。
首先判断这个子串是不是回文,如果是回文,就加入在vector path中,path用来记录切割过的回文子串。
image.png

  1. public List<List<String>> partition(String s) {
  2. backtracking(s, 0);
  3. return res;
  4. }
  5. List<List<String>> res = new ArrayList<>(); // 记录所有组合结果
  6. LinkedList<String> path = new LinkedList<>(); // 记录一条路径
  7. public void backtracking(String s, int startIndex) {
  8. // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
  9. if (startIndex >= s.length()) { // 注意是等于也算,最后一层递归就是=
  10. res.add(new ArrayList<>(path));
  11. return;
  12. }
  13. for (int i = startIndex; i < s.length(); i++) {
  14. // 先判断是否为回文字串
  15. if (isPalindrome(s, startIndex, i)) {
  16. path.add(s.substring(startIndex, i + 1)); // 是回文字串则添加进去
  17. } else {
  18. continue; // 不是回文字串则查找下一个
  19. }
  20. backtracking(s, i + 1);
  21. path.removeLast();
  22. }
  23. }
  24. public boolean isPalindrome(String s, int start, int end) {
  25. int i, j;
  26. char[] chars = s.toCharArray();
  27. for (i = start, j = end; i < j; i++, j--) { // 双指针法判断回文数
  28. if (chars[i] != chars[j]) {
  29. return false;
  30. }
  31. }
  32. return true;
  33. }

93.复原IP地址

题目描述

image.png

解题思路

  • 递归参数

startIndex一定是需要的,因为不能重复分割,记录下一层递归分割的起始位置。
本题我们还需要一个变量pointNum,记录添加逗点的数量。

  • 递归终止条件

终止条件和131.分割回文串情况就不同了,本题明确要求只会分成4段,所以不能用切割线切到最后作为终止条件,而是分割的段数作为终止条件。
pointNum表示逗点数量,pointNum为3说明字符串分成了4段了。
然后验证一下第四段是否合法,如果合法就加入到结果集里

  • 单层搜索的逻辑

在每层如果有数字符合IP段的要求(也就是isValid函数返回为true),那么就加入一个 ‘.’ 在s字符 串中,pointNum加一,回溯也就是去掉字符串的 ‘.’ ,pointNum减一。不合法直接break,后面 的肯定都不合法,所以直接break,不是continue。
image.png

注意:需要判断ip是否大于12位。

  1. public List<String> restoreIpAddresses(String s) {
  2. if (s.length() > 12) { // 大于12位,不满足
  3. return res;
  4. }
  5. backtracking(s, 0, 0);
  6. return res;
  7. }
  8. List<String> res = new ArrayList<>();
  9. public void backtracking(String s, int startIndex, int pointNum) {
  10. if (pointNum == 3) { // 当 "." 的数量等于三时,退出循环
  11. // 此时判断最后一个字符串是否满足要求
  12. if (isValid(s, startIndex, s.length() - 1)) {
  13. res.add(s);
  14. }
  15. return;
  16. }
  17. for (int i = startIndex; i < s.length(); i++) {
  18. if (isValid(s, startIndex, i)) { // 由于判断时的左闭右闭区间
  19. s = s.substring(0, i + 1) + "." + s.substring(i + 1); // i的后面加入一个"."
  20. pointNum++;
  21. backtracking(s, i + 2, pointNum);
  22. pointNum--; // 回溯
  23. s = s.substring(0, i + 1) + s.substring(i + 2); // 去掉逗号
  24. } else {
  25. break; // 不合法,直接退出循环
  26. }
  27. }
  28. }
  29. /**
  30. * 判断字符串在左闭右闭的区间是否合法
  31. *
  32. * @param s
  33. * @param start
  34. * @param end
  35. * @return
  36. */
  37. public boolean isValid(String s, int start, int end) {
  38. if (start > end) {
  39. return false;
  40. }
  41. if (s.charAt(start) == '0' && start != end) { // 0开头的数字不合法
  42. return false;
  43. }
  44. int sum = 0;
  45. for (int i = start; i <= end; i++) {
  46. if (s.charAt(i) > '9' || s.charAt(i) < '0') { // 为非法字符
  47. return false;
  48. }
  49. sum = sum * 10 + (s.charAt(i) - '0'); // 请总和
  50. }
  51. if (sum > 255) { // 大于255不满足
  52. return false;
  53. }
  54. return true;
  55. }