【LeetCode】132.分割回文串 II

63 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

题目

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

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

示例 1

输入:s = "aab"
输出:1
解释:只需一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。

示例 2

输入:s = "a"
输出:0

示例 3

输入:s = "ab"
输出:1

提示

  • 1 <= s.length <= 2000
  • s 仅由小写英文字母组成

题解

思路

先对字符串进行预处理,isPal[j][i] 表示从字符 s[j] 到 s[i] 的子串是否回文,预处理分两种情况:

  1. 若子串字符在两个以内且俩字符相等,则该子串回文;
  2. 若子串的头尾两个字符相等,且中间部分回文,则该子串回文;

预处理完毕后,开始 DP,dp[i] 表示从字符串开头到第 i 个字符的最少回文分割次数,需要两重循环:

  1. 第一重循环:判断 s[0 ~ i] 子串本身是否为回文子串,若是,则最少分割次数为0;
  2. 第二重循环:计算状态转移方程,若 s[j ~ i] 是回文,则只需要在 s[j - 1] 和 s[j] 之间再做一次分割即可,即 dp[i] 为所有符合条件的j对应的 dp[j - 1] 的最小值加1。

代码

class Solution {
public:
    int minCut(string s) {
        int len = s.length();
        vector<vector<bool>> isPal(len, vector<bool>(len));

        for (int i = 0; i < len; ++ i)
        {
            for (int j = 0; j <= i; j ++ )
            {
                char ch1 = s[i], ch2 = s[j];
                //i <= j + 1 表示从s[j]到s[i]在两个字符以内
                if (ch1 == ch2 && (i <= j + 1 || isPal[j + 1][i - 1]))  
                {
                    isPal[j][i] = true;
                }
            }
        }

        vector<int> dp(len);
        for (int i = 0; i < len; ++ i)
        {
            if (isPal[0][i])
            {
                dp[i] = 0;      // s[0 ~ i] 本身就是回文子串,最少回文分割为 0
            }
            else
            {
                dp[i] = i;      // s[0 ~ i] 的最大回文分割次数为 i
                for (int j = 1; j <= i; ++ j)
                {
                    if (isPal[j][i])    //若 s[j ~ i]为回文
                    {                   //只需要在s[j - 1]和s[j]之间再做一次分割即可
                        dp[i] = min(dp[i], dp[j - 1] + 1);
                    }
                }
            }
        }

        return dp[len - 1];
    }
};

结语

业精于勤,荒于嬉;行成于思,毁于随。