本次题目
131 分割回文串
- 回文串:正读和反读都一样的字符串,可以使用双指针分别从前后向两边移动判断。
- 回溯:
- 定义全局变量(双向队列)存放回文子串,定义全局二维数组存放回文子串集合,使用双向队列定义for循环开始位置防止重复;
- 遍历完字符串后终止;
- 从startIndex位置(下一次遍历的开始位置)到for循环的i位置(左闭右闭,下次切割从i+1开始)不断切割,如果切割的子串是回文子串,则添加到双向队列中,否则直接跳过。
- 注意:字符串切割操作可以放到判断回文子串中。字符串切割(左闭右开)substring(start,end)。双向队列转为列表LinkedList(),都是用LinkedList容器。
class Solution {
//定义全局变量(双向队列)存放回文子串,定义全局二维数组存放回文子串集合
Deque<String> deque = new LinkedList();
List<List<String>> result = new LinkedList<>();
public List<List<String>> partition(String s) {
//使用startIndex定义for循环开始位置防止重复
int startIndex = 0;
//开始回溯
backtracking(s, startIndex);
//返回结果
return result;
}
//回溯函数
private void backtracking(String s, int startIndex){
//遍历完字符串后终止
if(startIndex > s.length() - 1){
//栈转为List
result.add(new LinkedList(deque));
return;
}
//从startIndex位置(下一次遍历的开始位置)
//到for循环的i位置(左闭右闭,下次切割从i+1开始)不断切割
for(int i = startIndex; i < s.length(); i++){
//如果切割的子串是回文子串,则添加到双向队列中,否则直接跳过
if(isPalindrome(s, startIndex, i)){
String res = s.substring(startIndex, i + 1);
deque.addLast(res);
}else{
continue;
}
//回溯
backtracking(s, i + 1);
deque.removeLast();
}
}
//判断回文串
private boolean isPalindrome(String s, int start, int end){
//使用双指针分别从前后向两边移动判断
for(int i = start, j = end; i < j; i++, j--){
if(s.charAt(i) != s.charAt(j)) return false;
}
return true;
}
}
93 复原IP地址
- 回溯:同上131 分割回文串,切割后判断是否满足ip地址的要求(<255)。
- 定义全局数组存储结果集合,由于不能重复分割,定义startIndex记录下一个for循环的起始位置,定义整数记录添加的点的数量(满足ip地址后已分割的次数);
- 当添加三个点(分割三次)时终止,此时若最后一段满足条件则将整个字符串(加点后)添加到结果集合中;
- 分割时同上131 分割回文串,分割范围为startIndex到i区间(左闭右闭),如果满足条件则在分割字符串后加上“.”,不满足条件则直接退出本次for循环,递归时下一次递归起始位置为i+2(因为在i+1添加了“.”)。
- 注意:判断分割的子串是否满足条件
- 不能以0开头(只有一个0是可以的);
- 不能有数字以外的其他字符;
- 不能大于255(注意防止溢出)。
class Solution {
//定义全局数组存储结果集合
List<String> result = new LinkedList<>();
public List<String> restoreIpAddresses(String s) {
//由于不能重复分割,定义startIndex记录下一个for循环的起始位置
int startIndex = 0;
//定义整数记录添加的点的数量(满足ip地址后已分割的次数)
int pointNum = 0;
//开始回溯
backtracking(s, startIndex, pointNum);
//返回结果
return result;
}
//回溯函数
private void backtracking(String s, int startIndex, int pointNum){
//当添加三个点(分割三次)时终止
if(pointNum == 3){
//此时若最后一段满足条件则将整个字符串(加点后)添加到结果集合中
if(judgeIP(s, startIndex, s.length() - 1)) result.add(s);
return;
}
//此处进行剪枝,i上限为3位,超过三位肯定超过255
for(int i = startIndex; i < Math.min(s.length(),startIndex + 3); i++){
//分割范围为startIndex到i区间(左闭右闭)
//如果满足条件则在分割字符串后加上“.”,不满足条件则直接退出本次for循环
if(judgeIP(s, startIndex, i)){
s = s.substring(0, i + 1) + "." + s.substring(i + 1, s.length());
pointNum++;
//回溯
//递归时下一次递归起始位置为i+2(因为在i+1添加了“.”)
backtracking(s, i + 2, pointNum);
s = s.substring(0, i + 1) + s.substring(i + 2, s.length());
pointNum--;
}else{
break;
}
}
}
//判断分割的子串是否满足条件
private boolean judgeIP(String s, int left, int right){
//防止溢出
if(right - left > 2) return false;
//当左边界大于右边界时返回false,此时点后无数
if(left > right) return false;
//(1)不能以0开头;
if(s.charAt(left) == '0' && left != right) return false;
//(2)不能有数字以外的其他字符;
int num = 0;
for(int i = left; i <= right; i++){
if(s.charAt(i) < '0' || s.charAt(i) > '9') return false;
num = num * 10 + (s.charAt(i) - '0');
}
//(3)不能大于255。
//当数字很大时溢出导致此处判断失败。在开头增加判断,超过三位则直接返回false
if(num > 255) return false;
return true;
}
}