1. 分割回文串
算法思路
1. 抽象为组合算法,采用递归,用startIndex进行切割;
2. 只有判断为回文子串,才将StringBuilder加入到path中;
注意点
判断回文子串可以用双指针法。
代码
class Solution {
List<List<String>> res = new ArrayList<List<String>>();
LinkedList<String> path = new LinkedList<>();
public List<List<String>> partition(String s) {
if(s == null) return res;
backtracking(s, 0, new StringBuilder());
return res;
}
private void backtracking(String s, int startIndex, StringBuilder sb){
if(startIndex == s.length()){
res.add(new ArrayList<>(path));
return;
}
for(int i = startIndex; i<s.length(); i++){
sb.append(s.charAt(i));
if(check(sb)){
path.add(sb.toString());
backtracking(s, i+1, new StringBuilder());
path.removeLast();
}
}
}
private boolean check(StringBuilder sb){
for(int i = 0; i< sb.length()/2; i++){
if (sb.charAt(i) != sb.charAt(sb.length()-i-1)) return false;
}
return true;
}
}
2. 复原IP地址
算法思路
1. 用startIndex将数字进行分割;
2. 若分割后的数字满足IP格式要求,再将该数字后面加 “.” ,继续递归;
3. 直至pointNum等于3,终止递归。
注意点
1. 循环 for (int i = start; i < sb.length(); i++) 应该是 for (int i = start; i <= end; i++),只需要计算 start 到 end 之间的数字,而不是整个 sb 的长度;
2. 在 isvalid 方法中,当 sb.charAt(start) == '0' && start!= end 时,应该使用单引号将 0 括起来,因为 char 类型需要使用单引号。
3. 因为有 “。” 的存在,startIndex每次需要加2。
代码
class Solution {
List<String> res = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
if(s == null) return res;
StringBuilder sb = new StringBuilder(s);
backtracking(sb, 0, 0);
return res;
}
private void backtracking(StringBuilder sb, int startIndex, int pointNum){
if(pointNum == 3){
if(isvalid(sb, startIndex, sb.length()-1)){
res.add(sb.toString());
}
return;
}
for(int i = startIndex; i<sb.length(); i++){
if(isvalid(sb, startIndex, i)){
sb.insert(i+1, '.');
pointNum += 1;
backtracking(sb, i+2, pointNum);
sb.deleteCharAt(i+1);
pointNum -= 1;
}
}
}
private boolean isvalid(StringBuilder sb, int start, int end){
if(start > end) return false;
if(sb.charAt(start) == '0' && start != end) return false;
int nums = 0;
for(int i = start; i<=end; i++){
int digit = sb.charAt(i)-'0';
nums = nums * 10 + digit;
if(nums > 255) return false;
}
return true;
}
}
3. 子集
算法思路
使用递归的方式,将每一个可能的子集都放进结果集即可。
注意点
如果判断条件是startIndex >= nums.length,则需要加path.add操作放在之前,否则会漏掉自己。
代码
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> subsets(int[] nums) {
if (nums.length == 0) return res;
backtracking(nums, 0);
return res;
}
private void backtracking(int[] nums, int startIndex){
res.add(new ArrayList<>(path));
if(startIndex >= nums.length) return;
for(int i=startIndex; i<nums.length; i++){
path.add(nums[i]);
backtracking(nums, i+1);
path.removeLast();
}
}
}
4. 子集 II
算法思路
与前面的子集相同,就是要做去重
注意点
去重操作是要判断used[i-1]是否为false,如果是,说明 i-1 已经回溯了,此时属于同层去重。
代码
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
if(nums.length == 0) return res;
Arrays.sort(nums);
boolean[] used = new boolean[nums.length];
Arrays.fill(used, false);
backtracking(nums, 0, used);
return res;
}
private void backtracking(int[] nums, int startIndex, boolean[] used){
res.add(new ArrayList<>(path));
if(startIndex >= nums.length) return;
for(int i = startIndex; i<nums.length; i++){
if(i>0 && nums[i] == nums[i-1] && used[i-1] == false){
continue;
}
path.add(nums[i]);
used[i] = true;
backtracking(nums, i+1, used);
path.removeLast();
used[i] = false;
}
}
}