动态规划经典问题---最少回文分割

题目
输入一个字符串,请问至少需要分割几次才可以使分割出的每个子字符串都是回文?例如,输入字符串"aaba",至少需要分割1次,从两个相邻字符’a’中间切一刀将字符串分割成两个回文子字符串"a"和"aba"。

分析
完成一件事需要多个步骤,而且每步可能面临多个选择,这个问题看起来需要用回溯法解决。但由于这个问题没有要求列出所有符合要求的分割方法,而是只需要计算出最少的分割次数,因此这个问题更适合用动态规划来解决。
如果子字符串S[0…i]本身就是一个回文,那么不需要分割就符合要求,此时f(i)等于0。如果子字符串S[0…i]不是一个回文,那么对每个下标j(1≤j≤i)逐一判断子字符串S[j…i]是不是回文。如果是回文,那么这就是一个有效的分割方法,此时的分割次数相当于子字符串S[0…j-1]的分割次数再加1,因为这是将子字符串S[0…j-1]按照要求分割之后再在S[j-1]和S[j]这两个字符中间再分割一次。因此,f(i)就是所有符合条件的j对应的f(j-1)的最小值加1。

对于字符串

"aabacd"
先算出
dp[1] --- "a"
dp[2] --- "aa"
dp[3] --- "aab"
随着字符串的增大,dp[n]的值不断增大
[0, 0, 1, 1, 2, 2, 2, 3, 4, 5, 6, 6, 6]
6

在判断的时候,先给出最差情况
 dp[i] = i;// 每个字符分割一刀,表示最大值
然后判断string[j...i],是不是回文,是就dp[j-1]+1,
不是就 dp[j - 1] + i - j + 1

代码

import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        int result = minCut("aabacccfgaccc");
        System.out.println(result);
    }
    public static int minCut(String s) {
        int len = s.length();
        boolean[][] isPal = new boolean[len][len];// 记录S[j...i]字符串是不是回文
        for (int i = 0; i < len; i++) {
            for (int j = 0; j <= i; j++) {
                char ch1 = s.charAt(i);
                char ch2 = s.charAt(j);
                // isPal[j+1][i-1] == true: 表示s[j+1...i-1]是回文字符串
                // i<= j+1: 表示i=j或者j=i-1
                if (ch1 == ch2 && (i <= j + 1 || isPal[j + 1][i - 1])) {
                    isPal[j][i] = true;//
                }
            }
        }

        int[] dp = new int[len];
        for (int i = 0; i < len; i++) {
            if (isPal[0][i]) {
                dp[i] = 0;
            }
            else {
                dp[i] = i;// 每个字符分割一刀,表示最大值
                for (int j = 1; j <= i; j++) {
                    if (isPal[j][i]) {// 在j和j-1中间分割一刀
                        dp[i] = Math.min(dp[i], dp[j - 1] + 1);//遍历从1到i,分割方法的最小值
                    }
                    else{// 其实这个分支可以省略,因为dp[i] = i;
                        dp[i] = Math.min(dp[i], dp[j - 1] + i - j + 1);
                    }
                }
            }
        }
        System.out.println(Arrays.toString(dp));
        return dp[len - 1];
    }
}



  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划分割回文串是一种常用的解决方案。在动态规划中,我们可以使用不同的状态定义和状态转移方程来解决这个问题。 一种常见的状态定义是使用一维数组dp[i],其中dp[i]表示字符串s的前i个字符形成回文子串的最少分割次数。这种定义可以通过判断s[j:i]是否为回文来进行状态转移,其中1 <= j <= i。具体的状态转移方程可以如下表示: - 当s[0:i]本身就是一个回文串时,不需要进行分割,即dp[i] = 0。 - 否则,我们可以遍历所有可能的分割点j,如果s[j+1:i]是回文串,那么我们可以将问题分割为两部分,即dp[i] = min(dp[i], dp[j] + 1)。 另一种状态定义是使用二维数组dp[i][j],其中dp[i][j]表示字符串s的前i个字符分割为j个子串的修改的最小字符数。在这种定义下,我们可以使用类似的状态转移方程来进行计算。具体的状态转移方程可以如下表示: - 当i < j时,不可能将前i个字符分割为j个子串,即dp[i][j] = INF。 - 当i >= j时,我们可以遍历所有可能的分割点k,计算dp[i][j]的最小值,即dp[i][j] = min(dp[i][j], dp[k][j-1] + cost(k+1, i)),其中cost(k+1, i)表示将子串s[k+1:i]修改为回文所需的最小字符数。 这两种定义和状态转移方程都可以用来解决动态规划分割回文串问题,具体使用哪种方法取决于具体的问题要求和效率要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [动态规划解决回文串问题](https://blog.csdn.net/qq_37414405/article/details/111317301)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [动态规划分割回文串](https://blog.csdn.net/melody157398/article/details/119769501)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值