动态规划 回文篇

回文字符串指的是字符串从左到右 和从右到左是一样的。
对于从i到j的子串,判断其回文
s[i]==s[j] &&(flag[i+1][j-1] || j-i<=1)
1、最小回文分割次数
求最小的切分,使得分割后每个子串都是回文的。
状态 dp[i] 表示从i到n-1的最小回文分割数目,
那么对于任意的一个j,j属于[i,n-1],如果[i,j]是回文的话,则
dp[i]=min(dp[j+1]+1) j从i到n-1,且当j=n-1时,dp[n]=-1;
因此只要遍历一遍从i到n-1的j,求取最小的dp[i]即可

//和最长递增子序列很相似
    public int minCut(String s) {
        int len=s.length();
        boolean[][] flag=new boolean[len][len];
        //dp[i]代表[i,len-1]的切成回文的最小分割数
        int[] dp=new int[len+1];
//        getdp(s,flag,len);
        //从右往左进行计算
        dp[len]=-1;
        for(int i=len-1;i>=0;i--){
            dp[i]=Integer.MAX_VALUE;
            for(int j=i;j<len;j++){
                //[i,j]是回文
                if(s.charAt(j)==s.charAt(i) && (j-i<=1 || flag[i+1][j-1])){
                    flag[i][j]=true;
                    dp[i]=Math.min(dp[j+1]+1,dp[i]);
                }
            }
        }
        return dp[0];
    }

如果要求出所有的分割情况,而不要求最小的,则应该使用递归回溯

public static List<List<String>> partition(String s) {
        List<List<String>> res=new ArrayList<>();
        List<String> cur=new ArrayList<>();
        dfs(res,cur,s);
        return res;
    }
    /**
     *
     * @param res 存储每一趟遍历的符合要求的记过
     * @param cur cur记录一趟遍历,存储的结果
     * @param s
     */
    public static void dfs(List<List<String>>res,List<String> cur,String s){

        if(s.length()==0){
            res.add(new ArrayList<>(cur));
        }
        int len=s.length();
        for(int i=1;i<=len;i++){
            String s1=s.substring(0,i);
            if(isPalindrome(s1)){
                cur.add(s1);
                String s2=s.substring(i);
                dfs(res,cur,s2);
                cur.remove(cur.size()-1);
            }
        }
    }

    public static boolean isPalindrome(String s){
        if(s==null||s.length()==0){
            return false;
        }
        int j=s.length()-1;
        int i=0;
        while(i<j){
            if(s.charAt(i)==s.charAt(j)){
                i++;
                j--;
            }else{
                return false;
            }
        }
        return true;
    }

2、最长回文子串
可以借用1中的方式,求出一个dp数组,保存[i,j]是否为回文的信息,对于一个从i到j的子串,如何判断它是否为回文串

由此可见dp[i][j]依赖于dp[i+1][j-1],我们要计算出dp[i][j]必须先求出dp[i+1][j-1],而这个位置是在(i,j)的下方,因此我们填充dp这个表格数组的时候遵从的是从做到有从下到上的生成方式。

//为什么不能从i=0到i=n-1 而是要从i=n-1到i=0因
    //flag[i][j]依赖于 flag[i+1][j-1]因此 要先计算出i+1行 ,j-1列才能推算出i,j
    //因此方向为从下到上,从左到右
    public String longestPalindrome(String s) {
        if(s==null || s.length()==0){
            return "";
        }
        int n=s.length();
        boolean[][] flag=new boolean[n][n];
        int[] dp=new int[n];
        for(int i=0;i<n;i++){
            dp[i]=1;
        }
        char[]t =s.toCharArray();
        //记录长度
        int res=0;
        //记录起始位置
        int index=-1;
        for(int i=n-1;i>=0;i--){
            for(int j=i;j<n;j++){
                //[i,j]是回文,dp[i]表示[i,len-1]上的最长回文串,dp[i]有一个初始值
                //现在找到了一个 i~j 长度为j-i+1,  j一直变化,能够找到以i开头的最大长度
                if(t[i]==t[j]&& (j-i<=1||flag[i+1][j-1])){
                    flag[i][j]=true;
                    //从i到j是回文,那么[i,len-1]的最长回文 至少有j-i+1;
                    dp[i]=Math.max(dp[i],j-i+1);
                }
            }
            if(res<dp[i]){
                res=dp[i];
                index=i;
            }
        }
        return s.substring(index,index+res);
    }
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页