题目一:131. 分割回文串
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是 回文串 。返回 s
所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
解法:回溯+剪枝
递归用来纵向遍历,for循环用来横向遍历,切割线(就是图中的红线)切割到字符串的结尾位置,说明找到了一个切割方法。
此时可以发现,切割问题的回溯搜索的过程和组合问题的回溯搜索的过程是差不多的。
代码和提交结果如下:
class Solution {
public List<List<String>> partition(String s) {
List<List<String>> res = new ArrayList();
Deque<String> paths = new LinkedList();
backTracking(res,paths,s,0);
return res;
}
private void backTracking(List<List<String>> res , Deque<String> paths , String s , int startIndex){
if(startIndex == s.length()){
res.add(new ArrayList(paths));
return;
}
for(int i = startIndex ; i < s.length() ; i++){
if(isPalindrome(s,startIndex,i)){
paths.addLast(s.substring(startIndex,i+1));
backTracking(res,paths,s,i+1);
paths.removeLast();
}else{
continue;
}
}
}
private boolean isPalindrome(String s , int start , int end){
while(start < end){
if(s.charAt(start) == s.charAt(end)){
start++;
end--;
}else{
return false;
}
}
return true;
}
}
总结: 和组合问题一样,用 startIndex 作为分割线,进入递归函数之后,首先需要判断是否是回文串,如果是,再执行下一次递归。递归的判出条件为,startIndex到了最后,也就是到了叶子节点。
问题二:93. 复原 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 ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你不能重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
解题思路: 这个题也是分割字符串的题,只不过有两点不同就是 判出条件有限制,总共有三个 (.),所以需要一个变量用来记录(.)的个数,当(.)的个数 == 3 时,判断剩余的数字组合符合不符合要求,如果符合则添加,然后return,不符合直接return。
我用了两套代码来实现这个题,思想是一致的。
代码一思想及提交结果:直接在String字符串本身进行修改,然后完成回溯。
class Solution {
public List<String> restoreIpAddresses(String s) {
List<String> res = new ArrayList<>();
if (s.length() > 12) return res;
backTracking(s,res,0,0);
return res;
}
private void backTracking(String s ,List<String> res , int startIndex , int nums){
if(nums == 3){
if(isValue(s,startIndex,s.length()-1)){
res.add(s);
}
return;
}
for(int i = startIndex ; i < s.length() ; i++){
if(isValue(s,startIndex,i)){
s = s.substring(0,i+1) + "." + s.substring(i+1);
nums += 1;
backTracking(s,res,i+2,nums);
nums -= 1;
s = s.substring(0,i+1) + s.substring(i+2);
}else{
break;
}
}
}
private boolean isValue(String s,int start,int end){
if(start > end){
return false;
}
if(s.charAt(start) == '0' && start < end){
return false;
}
int num = 0;
for(int i = start ; i <= end ; i++){
num = num*10 + (s.charAt(i) - '0');
}
if(num > 255){
return false;
}
return true;
}
}
代码二思想及提交结果:用Stringbulider来记录路径完成回溯。
class Solution {
public List<String> restoreIpAddresses(String s) {
List<String> res = new ArrayList<>();
StringBuilder paths = new StringBuilder();
if (s.length() > 12) return res;
backTracking(s,res,0,0,paths);
return res;
}
private void backTracking(String s ,List<String> res , int startIndex , int nums , StringBuilder paths){
if(nums == 3){
if(isValue(s,startIndex,s.length()-1)){
int size = s.length() - startIndex;
paths.append(s.substring(startIndex,s.length()));
res.add(new String(paths));
paths.delete(paths.length()-size,paths.length());
}
return;
}
for(int i = startIndex ; i < s.length() ; i++){
if(isValue(s,startIndex,i)){
int size = i+1-startIndex;
paths.append(s.substring(startIndex,i+1));
paths.append(".");
nums += 1;
backTracking(s,res,i+1,nums,paths);
nums -= 1;
paths.deleteCharAt(paths.length()-1);
paths.delete(paths.length()-size,paths.length());
}else{
break;
}
}
}
private boolean isValue(String s,int start,int end){
if(start > end){
return false;
}
if(s.charAt(start) == '0' && start < end){
return false;
}
int num = 0;
for(int i = start ; i <= end ; i++){
num = num*10 + (s.charAt(i) - '0');
}
if(num > 255){
return false;
}
return true;
}
}
总结:我们会发现方法二比方法一快了很多,这是因为StringBuilder比String快的原因。