回文最小分割数

题目:给定一个字符串str,返回把str全部切成回文子串的最小分割数;
举例子:

str == “ABA” 返回0
str == “ACDCDCDAD" 返回2,最少切两次;“A”“CDCDC”“DAD”;切两下分成三个回文子串;

解题思路:

此题解题思路是这样的,假设给你一串字符"ACDFFDCE",我们可以直接看出此最少需要切两次,成三个子串"A"“CCFFCC”“E”,
可是我们也可以看出中间的子串"CCFFCC"还是可以切成三个回文子串的,可是我们要求最小切割数,所以切两次成三个回文子串
是最少的切割方法;
那么怎么实现呢?你怎么知道中间切成"CCFFCC"才会有最小的切割次数?
此时就要加一层循环来判断取得当前字符到 len-1 处整个子串的最少切割次数了;

举个例子:

ADCDEBE

  1. 首先两个指针i j 两次循环从后面开始遍历,dp数组dp[len]处的值为-1是有原因的, 就是从最末尾一个字符开始遍历的时候,E元素到len-1处的子串,最少切割次数为dp[6+1]+1=0; 刚好符合要求,后面的以此类推,设置p[6][6] =true,意思是下标6----6所在的子串就是回文串
  2. B元素的时候,最少切割次数为E的下一个字符所到 len-1处的子串最少切割次数加1,然后j从i开始遍历,判断B元素能不能跟后面子串中的那几个子串组成回文子串,循环结束后是没有,此时dp[5] =dp[6]+1=1;设置p[5][5]=true;
  3. i再次减一,此时继续j开始从i=4,第五个元素"E"处开始遍历,j=4时,dp[4] = dp[5]+1 =
    2+1=2;j=5时,第五个元素"E"跟第六个元素"B"不等,所以他们直接不能组成回文;j继续遍历,j=6s时,发现下标为6的元素也是"E",然后此时再次判断这两者之间的子串是不是回文串,它两之间就是下标[5],之前的boolean元素已经判断过,p[5][5]=true,所以进入if语句,将当前的子串p[4][6]设置为true,代表4–6的子串就是个回文子串,然后求最小切割次数,就是下标为6元素的后面的子串的最少切割次数(上一步中已经求出)
  4. 后面的依次类推,最后dp[0]的值就是最少切割次数;
下面说一下如何迅速判断子串是否为回文子串:
  1. 首先,一个元素就是一个回文子串
  2. 第二,如果两个元素下标相邻,并且相同,它两是个回文子串
  3. 第三,两个元素相同,但是长度大于2,假设下标分别为i和j,那么就判断i+1----j-1的子串是不是回文串,而我们在i从后向前遍历的过程中,每次碰到回文子串,都会用boolean二维数组存下来那个子串,所以此刻之间判断即可,如果此时boolean[i+1][j-1]不是true,那么当前i和j所在下标组成的子串不是回文串;
代码解答
/**
 * @author Dangxuchao
 * @Title: MinCut
 * @ProjectName 第五章:字符串问题
 * @Description: 回文最小分割数
 *
 * @date 2019/7/2 16:16
 */
public class MinCut {
    public static void main(String[] args) {
        String str1 = "ACDCDCE";
        String str2 = "ABCDEFF";
        String str3 = "AAAAABB";
        String str4 = "AAAAAAA";
        System.out.println(isMinCut(str1));
        System.out.println(isMinCut(str2));
        System.out.println(isMinCut(str3));
        System.out.println(isMinCut(str4));

    }
    public static int isMinCut(String str){
        //首先定义一个数组dp用来存放最小切割的次数;
        char[] chars = str.toCharArray();
        int len = chars.length;
        int[] dp = new int[len+1];

        //boolean一个二维数组;在计算dp数组的过程中,
        // 可以迅速判断出子串是否为回文串;
        //boolean数组的初始值为false;
        boolean[][] p = new boolean[len][len];
        dp[len] = -1;
        //然后两个指针从后向前遍历,每次记录下能切割成最大回文子串的次数;
        for (int i = len-1;i >= 0;i--){
        
            /*每次进来将当前的dp[i]设置为最大值,
            //内层循环结束后得出dp[i]的最小值,
            这个最小值就是str串中下标i至len-1串的切成回文数的最小切割次数;*/
            
            dp[i] = Integer.MAX_VALUE;
            
            /*内层循环,就是说,从当前i所处的字符开始,
            j一直遍历,因为i每次往前挪一个位置,就是减1,
            减一之后,一开始的最少切割次数就是上次i内层循环
            完之后得出来的最少切割次数+1,简单来说就是相当于把当前元素
            直接单独切割出来;
            然后往后遍历,如果能碰到哪个字符之后前后连接起来刚好凑成回文串
            那么最少切割次数就是那个字符得下一个dp的值加1了,就是内层循环当前
            计算出来的最少切割次数,
            以此类推,最终dp[0]的值就是整个长串的最少回文分割数了;*/
            
            for (int j = i;j < len;j++){
                if (chars[i] == chars[j]&&(j-i<2 || p[i+1][j-1])){
                
                    //如果为true,那么代表子串从i到j的子串就是一个回文子串;
                    
                    p[i][j] = true;

                    /*然后求最小分割次数,也就是j字符后面的子串的分割次数加上1,
                    因为从i到j是个回文子串
                    此处要取dp[i]和dp[j+1]+1之间的最小值*/
                    
                    dp[i] = Math.min(dp[i],dp[j+1]+1);
                }
            }
        }
        return dp[0];
    }
}
/*
     输出结果:
        2
        5
        1
        0
 */
结果:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值