在问 · 算法题解

132. 分割回文串 II

困难动态规划回文表+动态规划力扣原题 ↗

题目描述

输入一个字符串 s。在它上面切若干刀,把它分成几个连续片段,使每个片段都是回文串。

返回最少需要切几刀。如果 s 本身就是回文,返回 0。

示例 1
输入:s = "aab"
输出:1

解释:切一刀得到 aa 和 b。

示例 2
输入:s = "a"
输出:0

解释:单个字符本身是回文,不用切。

示例 3
输入:s = "ab"
输出:1

解释:切一刀得到 a 和 b。

数据范围

  • 1 <= s.length <= 2000
  • s 只含小写英文字母。

解题思路:回文表+动态规划

两张表一起填。pal[i][j] 表示 s[i..j] 是否回文:s[i] == s[j] 且(长度 ≤ 2 或 pal[i+1][j-1])。dp[j] 表示 s[0..j] 的最少刀数,初始为 j(每个字符切一刀)。枚举最后一段回文 s[i..j]:若 i = 0,整段回文,dp[j] = 0;否则 dp[j] = min(dp[j], dp[i-1] + 1)。

为什么正确

外层按 j 增大,内层 i 从小到大;用到的 pal[i+1][j-1] 属于列 j-1,已经算好。

复杂度分析

时间 O(n²),空间 O(n²)。

易错点

每次重新判断子串是否回文会变成 O(n³),对 n = 2000 会超时;要用 pal 表记住结果。答案是刀数,不是段数,段数 = 刀数 + 1。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def minCut(self, s: str) -> int:
        n = len(s)
        pal = [[False] * n for _ in range(n)]
        dp = list(range(n))
        for j in range(n):
            for i in range(j + 1):
                if s[i] == s[j] and (j - i < 2 or pal[i + 1][j - 1]):
                    pal[i][j] = True
                    if i == 0:
                        dp[j] = 0
                    else:
                        dp[j] = min(dp[j], dp[i - 1] + 1)
        return dp[n - 1]

载入编辑器,自己跑一遍 →

其他解法

两种解法时间都是 O(n²);回文表法结构直观,中心扩展法把空间从 O(n²) 降到 O(n)。

解法 2:中心扩展+动态规划 时间 O(n²),空间 O(n)

dp[j] 表示 s[0..j] 的最少分割次数,初始 dp[j] = j。枚举每个中心,分奇数长度和偶数长度向两边扩展。每扩展出一个回文 s[l..r],就用 dp[l-1] + 1 更新 dp[r],l = 0 时 dp[r] = 0。中心从左到右处理,用到的 dp[l-1] 都已经确定。

不需要 n×n 的回文表,空间降到 O(n)。正确性依赖中心的处理顺序,理解难度略高。

class Solution:
    def minCut(self, s: str) -> int:
        n = len(s)
        dp = list(range(n))
        for c in range(n):
            for l, r in ((c, c), (c, c + 1)):
                while l >= 0 and r < n and s[l] == s[r]:
                    dp[r] = 0 if l == 0 else min(dp[r], dp[l - 1] + 1)
                    l -= 1
                    r += 1
        return dp[n - 1]

动态规划 专题的其他题目

查看 动态规划 全部题目 →