LeetCode 132. 分割回文串 II(经典必会)

LeetCode 132. 分割回文串 II

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

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

示例 1:

输入:s = "aab"

输出:1

解释:只需一次分割就可将 s 分割成 "aa","b" 这样两个回文子串。

示例 2:

输入:s = "a"

输出:0

示例 3:

输入:s = "ab"

输出:1

提示:

1 <= s.length <= 2000

s 仅由小写英文字母组成

动态规划

python 复制代码
class Solution:
    def minCut(self, s: str) -> int:
        # dp[i] 表示从第i个字符构成的字符串可以划分成的最少子回文串,字符从0开始计数
        # dp[i] = min(序列S) S中元素为 dp[k] + 1  k from 0 to i-1 且 s[k+1:i]为回文串
        # 每种情况能否成立需要知道+1是否成立,也就是需要知道,右边剩余字符串是否是回文子串
        # 那么如何求的s中任意连续字串是否为回文串呢?

        n = len(s)
        judge = [[False] * n for _ in range(n)]
        for i in range(n - 1):
            g, h = i, i + 1
            while -1 < g < h < n and s[g] == s[h]:
                judge[g][h] = True
                g -= 1
                h += 1
        for i in range(n):
            judge[i][i] = True
            g, h = i - 1, i + 1
            while -1 < g < h < n and s[g] == s[h]:
                judge[g][h] = True
                g -= 1
                h += 1

        dp = [i for i in range(1, n + 1)]
        for i in range(n):
            if judge[0][i]:
                dp[i] = 1

        for i in range(1, n):
            for j in range(i):
                if judge[j + 1][i]:
                    dp[i] = min(dp[i], dp[j] + 1)
        return dp[n - 1] - 1

时间复杂度: O ( n 2 ) O(n^2) O(n2)。计算字符串中所有回文串耗时 O ( n ) O(n) O(n)。动态规划计算最少划分次数外层循环次数函数为 l a y e r 1 ( n ) = n layer1(n)=n layer1(n)=n,求面积得耗时 O ( n ∗ n / 2 ) = O ( n 2 ) O(n*n/2)=O(n^2) O(n∗n/2)=O(n2)。

空间复杂度:字符串回文串判定消耗 O ( n 2 ) O(n^2) O(n2),动态规划dp数组消耗 O ( n ) O(n) O(n),空间复杂度 O ( n 2 ) O(n^2) O(n2)。

本题动态规划的思路相对好想,但是关于字符串中所有回文串的判定处理不好会导致时间复杂度过高,导致超时,预处理序列是一种常用的以空间换时间的方法。下面总结一下本题以及一些拓展:

在 O ( n 2 ) O(n^2) O(n2)耗时求出字符串中所有连续字符子串是否为回文串。

设dpij表示从第i个字符到第j个字符构成的字串是否为回文串,字符从0开始计数

最经典的分成两种情况讨论。

若回文串为偶数长度,则最中间两个数往外扩展一定对称,所以长度为n的字符串中连续两个数会有n-1对,对每一对数据进行判断即可

若回文串为奇数长度,则选一个中间数字往两边扩展即可,共有n中选法。

python 复制代码
n = len(s)
judge = [[False] * n for _ in range(n)]
for i in range(n - 1):
    g, h = i, i + 1
    while -1 < g < h < n and s[g] == s[h]:
        judge[g][h] = True
        g -= 1
        h += 1
        
for i in range(n):
    judge[i][i] = True
    g, h = i - 1, i + 1
    while -1 < g < h < n and s[g] == s[h]:
        judge[g][h] = True
        g -= 1
        h += 1

而事实上,这里有一种很巧妙地方式可以合并上面两种情况。

奇数和偶数的区别在于,有没有中间值,如果只有一个字符,那么一定是回文串,假设不考虑中间到底有没有字符,那么一个回文串最外面的左右字符一定得相同。假设左边是第i个字符,右边是第j个字符,那么dpi == dpj,若i到j构成回文串那么i+1到j-1也一定构成,则有

dpij = dpi == dpj and dpi+1j-1

不过这里有一些特殊情况:

  1. 当i==j时,dpi+1j-1中i+1>j-1导致用到了一个无意义的状态
  2. 当i+1==j时,同理,也用到了一个无意义状态
    不过巧合的是,当用到无意义状态的时候,只需要看dpi == dpj 即可,所以这里等效可以把无意义状态全部置为True。

则会有完整状态转移方程。

python 复制代码
# 由于在计算dp[i][j]的时候不会用到dp[i][j]本身的值,所以可以同过全部赋值True的方式简化赋初值
# 只需要计算矩阵的一半即可,对角线也无需计算
n = len(s)
judge = [[True] * n for _ in range(n)]
for i in range(n - 1):
    for j in range(i + , n):
        judge[i][j] = judge[i + 1][j - 1] and s[i] == s[j]

另外一个场景

给定数字序列,从第i个数字到第j个数字之和,求出所有情况的和。

蛮力就是三层循环,第一层i,第二层j,第三层枚举求和

但实际上可以优化,求和即可,dpi为第0到第i个数字的和那么fij = dpj - dpi-1

这样只需要O(n)时间就可以构造好fij的结果,O(n)通常不会影响到核心解题逻辑的时间复杂度。

还有类似求最大值,给定数组序列,fi为除第i个数之外所有数的最大值,最小值也同理。只需遍历一次找到最大值和次大值,第i个数不是最大值fi就是最大值,第i个数是最大值fi就是次大值。

又如前后缀数组等等。

相关推荐
欧阳x天几秒前
八大排序算法(C语言实现)
数据结构·算法·排序算法
爱睡懒觉的焦糖玛奇朵3 分钟前
【从视频到数据集:焦糖玛奇朵的魔法工具Dataset Cleaner】
人工智能·python·学习·算法·yolo·音视频
xjxijd4 分钟前
行为感知算法赋能运维,提前预判硬件故障与异常访问
运维·算法
江屿风7 分钟前
C++图论基础拓扑排序经典OJ题流食般投喂
开发语言·c++·笔记·算法·图论
C+-C资深大佬10 分钟前
C++ 数字与字符串互转
java·c++·算法
满怀冰雪16 分钟前
第12篇-二分答案法-当答案不好求时如何反向搜索
java·算法
KaMeidebaby18 分钟前
卡梅德生物技术快报|兔单克隆抗体应用实战:禽源病原 IFA 检测全流程拆解
前端·人工智能·物联网·算法·百度
CC数学建模21 分钟前
2026年第十六届APMCM 亚太地区大学生数学建模竞赛(中文赛项)赛题A题:自来水厂水质预测与评估完整思路、代码、模型、文章,全网首发高质量分享!
python·算法·数学建模
折哥的程序人生 · 物流技术专研8 小时前
Java面试85题图解版 · 特别篇:2026后端高频面试题复盘(算法底层逻辑+高并发架构设计全解析,附Java实战代码)
java·网络·数据库·算法·面试
想吃火锅100510 小时前
【leetcode】14.最长公共前缀js
算法·leetcode·职场和发展