Lintcode:136.切割回文串 VS 108. 切割回文串II(考点:回溯法、DFS、DP)

136. 切割回文串

描述

给定一个字符串s,将s分割成一些子串,使每个子串都是回文串。

返回s所有可能的回文串分割方案。

样例

给出 s = "aab",返回

[
  ["aa", "b"],
  ["a", "a", "b"]
]

分析:本题采用回溯法,深度优先遍历字符串。注意保存子结果的条件以及每次返回上一层时(回溯时),需要把子结果中的在该层添加的(最后添加的)删掉再返回上一层。

public class Solution {
    /*
     * @param s: A string
     * @return: A list of lists of string
     */
    public List<List<String>> partition(String s) {
        // write your code here
        List<List<String>> res=new ArrayList<>();
        if(s==null||s.length()==0)  return res;
        List<String> partion=new ArrayList<>();
        helper(s,0,partion,res);
        return res;
    }

    private void helper(String s,int start,List<String> partion,List<List<String>> res){
        if(start==s.length()) {
            res.add(new ArrayList<>(partion));
            return;
        }
        for(int i=start;i<s.length();i++){
            String s1=s.substring(start,i+1); //前半段
            if(!isPalindrome(s1)){
                continue;
            }
            partion.add(s1);
            helper(s,i+1,partion,res);  //后半段
            partion.remove(partion.size()-1);  //return后执行,返回上一层(回溯),所以要把在改层加入partion的去除掉
        }
    }

    private boolean isPalindrome(String s){  //判断是否是回文串
        int i=0,j=s.length()-1;
        while(i<j){
            if(s.charAt(i)!=s.charAt(j)){
                return false;
            }
            i++;
            j--;
        }
        return true;
    }
}

108. 切割回文串II

描述

给定一个字符串s,将s分割成一些子串,使每个子串都是回文。

返回s符合要求的的最少分割次数。

样例

比如,给出字符串s = "aab"

返回 1, 因为进行一次分割可以将字符串s分割成["aa","b"]这样两个回文子串

分析:本题采用动态规划,首先构建出二维数组isPalindrome,isPalindrome[i][j]表示是下标从i到下标为j之间构成的字符串是否是回文串。定义数组int[] f=new int[s.length()+1]表示前i个字母,最少切割几次可以切割为都是回文串。i从1开始到s.length(),然后j从0开始到i-1结束,如果isPalindrome[j][i-1]是回文串,即s[j]-s[i-1]之间是回文串,则最少只需要1次切割(对半切割)即可分成两个回文串,则前i个字母所需要的最小切割次数为f[i]和前j个字母所需最小切割次数f[j]+1之间的较小值。所以f[i]=min(f[i],f[j]+1).

public class Solution {
    /**
     * @param s: A string
     * @return: An integer
     */
    public int minCut(String s) {
        // write your code here
        if(s==null||s.length()==0)  return 0;
        boolean[][] IsPalindrome=getIsPalindrome(s);
        int[] f=new int[s.length()+1];  //f[i]表示前i个字母,最少切割几次可以切割为都是回文串
        for(int i=0;i<=s.length();i++)  //初始化,切割成一个个的字母,如ab,切1次为a,b
            f[i]=i-1;
        for(int i=1;i<=s.length();i++){
            for(int j=0;j<i;j++){
                if(IsPalindrome[j][i-1]){   //如果IsPalindrome[j][i-1]是回文串,即s[j]-s[i-1]之间是回文串,则最少只需要1次切割(对半切割)即可分成两个回文串
                    if(f[i]>f[j]+1){
                        f[i]=f[j]+1;
                    }
                }
            }
        }
        return f[s.length()];
    }

    //isPalindrome[i][j]表示是下标从i到下标为j之间构成的字符串是否是回文串
    private boolean[][] getIsPalindrome(String s){
        boolean[][] isPalindrome=new boolean[s.length()][s.length()];
        for(int i=0;i<s.length();i++){
            isPalindrome[i][i]=true;
        }
        for(int i=0;i<s.length()-1;i++){
            if(s.charAt(i)==s.charAt(i+1))
                isPalindrome[i][i+1]=true;
        }
        for(int length=2;length<s.length();length++){
            for(int start=0;start+length<s.length();start++){
                isPalindrome[start][start+length]=isPalindrome[start+1][start+length-1] && s.charAt(start)==s.charAt(start+length);
            }
        }
        return isPalindrome;
    }

    private boolean isPalindrome(String s){
        int i=0,j=s.length()-1;
        while(i<j){
            if(s.charAt(i)!=s.charAt(j)){
                return false;
            }
            i++;
            j--;
        }
        return true;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_27139155/article/details/80996728