算法训练day26-回溯算法-分割回文串

39.组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

思路

先将该问题的解抽象为如下的树型结构,从中可以看出,当数组有序的情况下(小->大),如果当前的元素加上sum已经大于目标值target了,说明后续的分支已经没有符合的组合了,应该及时终止后续的分支展开,也就是所谓的剪枝处理。

另外,特别需要留意每个元素可以重复选用,那么在迭代时,下次遍历的开始元素下标仍然与上次相同。

40.组合总和II1

代码实现(带剪枝处理)-java

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
       // 结果集
       List<List<Integer>> res = new ArrayList<>();
       // 先对数组排序,方便后续做剪枝处理
       Arrays.sort(candidates);
       backtracking(res,new ArrayList<>(),candidates,target,0,0);
       return res;
    }


    /**
     *  回溯函数
     *  
     */
    private void backtracking(List<List<Integer>> res,List<Integer> path,int[] candidates,
                                                            int target , int sum,int  startIdx ){
       // 终止条件
       if(sum==target){
           res.add(new ArrayList(path));
           return;
       }
       // 递归加循环处理
       for(int i=startIdx;i<candidates.length;i++){
           // 剪枝处理:当sum加当前元素大于目标和,则直接终止遍历
           if(sum+candidates[i]>target)  break;
           
           // 正常处理,注意startIdx仍是从i开始,这样意味着当前元素能被重复选中
           path.add(candidates[i]);
           backtracking(res,path,candidates,target,sum+candidates[i],i);
           // 回溯:移除path中最后一个元素
           path.remove(path.size()-1);
       }
    }

}

40.组合总和II

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合。 

思路

本题与上题差别有两个:

1.每个元素不能重复选用

2.数组中本身包含相同的元素但是要求组合不能重复

以上两个差别决定了本题与上题处理上有所不同:

1.同一个元素不能重复使用,意味着下次迭代的数组下标位置应该加1

2.组合不能重复,意味着在同一树层中(排序后)相邻的相同元素要去重

代码实现-java

class Solution {
    // 结果集
    List<List<Integer>> res = new ArrayList<>();
    // 组合路径
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        // 数组排序
        Arrays.sort(candidates);
        backtracking(candidates,target,0,0);
        return res;
    }
    /**
     * 回溯函数 
     */
    private void backtracking(int[] candidates,int target,int sum,int startIdx){
        // 终止条件
        if(sum==target){
            res.add(new ArrayList<>(path));
            return;
        }
        // 遍历+递归
        for(int i=startIdx;i<candidates.length;i++){
            // 剪枝
            if(sum+candidates[i]>target) break;
            // 同一树层去重
            if(i>startIdx && candidates[i]==candidates[i-1]) continue;

            // 正常处理
            path.add(candidates[i]);
            backtracking(candidates,target,sum+candidates[i],i+1);
            // 回溯
            path.remove(path.size()-1);
        }
    } 
}

131.分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

回文串 是正着读和反着读都一样的字符串。

思路

切割问题本质上与组合问题相同,所以仍然可以抽象为一颗树的查找。

代码实现

class Solution {
    List<List<String>> res = new ArrayList<>();
    List<String> path = new ArrayList<>();
    public List<List<String>> partition(String s) {
       backtracking(s,0);
       return res;
    }
    private void backtracking(String s,int startIdx){
       // 终止条件:当切割线到达字符串末尾时,说明找到了回文子串
       if(startIdx >= s.length()){
           res.add(new ArrayList(path));
           return;
       }
       // 遍历
       for(int i=startIdx;i<s.length();i++){
            //如果是回文子串,则记录
            if (isPalindrome(s, startIdx, i)) {
                String str = s.substring(startIdx, i + 1);
                path.add(str);
            } else {
                continue;
            }
            //起始位置后移,保证不重复
            backtracking(s, i + 1);
            // 回溯
            path.remove(path.size()-1);
       }
    }
    /**
     * 判断是否回文子串(双指针法)
     */
     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;
     }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值