回文最少分割数

题目

给定一个字符串str,返回把str全部切成回文子串的最小分割数。

举例

str=“ABA”。本身是回文串,所以不需要切割,返回0。
str=“ACDCDCDAD”。最少需要切2次变成3个回文子串,比如“A”、“CDCDC”、“DAD”。返回2。

解答

经典动态规划。定义动态规划数组dp,dp[i]的含义是子串str[0…i]至少需要切割几次,才能把str[0…i]全部切割成回文串,所以dp[len-1]就是最终答案。
从左往右计算dp[i]的值,i初始为1(str[0]是单个字符,很显然是回文串,不需要切割,所以dp[0]=0),过程如下:

  1. 假设当前处在j位置(j<=i<len),如果str[j…i]是回文串,那么很显然dp[i]=dp[j-1]+1,意思是在str[0…i]这个子串上,str[j…i]已经是回文串了,那么它单独作为一个分割的部分,剩下的str[0…j-1]采用它最少的回文分割数,即dp[j-1]。
  2. 让j在0到i位置上枚举,所有符合情况中的最小值就是dp[i]的值,即dp[i] = min{dp[j-1]+1(0<=j<=i<len,且str[j…i]是回文串)}。
  3. 接下来判断str[j…i]是否是回文串,过程如下:
    3.1 定义一个二维数组boolean[][] p,p[j][i]==true代表str[j…i]是回文串。
    3.2 p[j][i]==true,一定是下面3种情况:
    str[j…i]只有一个字符。
    str[j…i]有2个字符并且这2个字符相等
    str[j+1…i-1]是回文串,即p[j+1][i-1]==true,并且str[j]==str[i]
  4. 在计算dp的过程中i是从左到右,j是从右到左遍历,所以p[j+1][i-1]一定已经计算过。
代码
public static int minCut(String str) {
    if (str == null || str.length() == 0) {
        return 0;
    }
    char[] cstr = str.toCharArray();
    int len = cstr.length;
    boolean[][] p = new boolean[len][len];
    int[] dp = new int[len];
    for (int i = 1; i < len; i++) {
        dp[i] = Integer.MAX_VALUE;
        for (int j = i; j >= 0; j--) {
            if (cstr[i] == cstr[j] && (i - j < 2 || p[j + 1][i - 1] == true)) {
                p[j][i] = true;
                //如果j-1<0即j==0,说明整个str[0...i]是回文串,所以dp[i]是0
                dp[i] = Math.min(dp[i], j - 1 >= 0 ? dp[j - 1] + 1 : 0);
            }
        }
    }
    return dp[len - 1];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃鱼的ねこ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值