LeetCode Hot100(56/100)——131. 分割回文串

文章目录

一、题目解析

给定一个字符串 s,需要将其分割为若干子串,使得每个子串都是 回文串。返回所有可能的分割方案。

示例:

复制代码
输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

我们需要枚举所有可能的切分方式,只保留每个部分都是回文串的组合。


二、问题分析与思考过程

2.1 回文串的定义

一个字符串如果从前往后读与从后往前读完全相同,即为回文串。例如:

  • "aba" 是回文串;
  • "abc" 不是回文串。

回文判断可以通过双指针实现:判断首尾字符是否相同,再逐步内缩。


2.2 思维导图

下面是题目的整体思考过程:
分隔回文串
回文判断
双指针判断是否为回文
可提前预处理
解法思路
回溯递归
从左到右枚举切分点
若前缀是回文 -> 递归剩余部分
动态规划
预处理回文判断表
提升效率
复杂度分析
时间复杂度较高(回溯)
可用DP优化回文检查
Java实现
递归 + 回溯
预处理优化


三、解法一:回溯(DFS)

3.1 思路原理

该题本质是枚举所有分割方式。

每次从字符串的开头开始,选择一个"切分点"作为当前子串的结束位置,如果该子串是回文,则递归处理剩下的部分。

核心思想:

  • 用一个临时列表 path 存当前的分割;
  • 当遍历到字符串末尾时,将一条完整方案加入结果集。

3.2 流程图示





开始
从索引 start 开始
start == s.length()?
加入当前方案到结果
循环 i 从 start 到 s.length()-1
是否为回文 sstart..i?
加入到 path
递归调用 dfs(i+1)
回溯:移除最后一个元素


3.3 时间复杂度与空间复杂度分析

  • 时间复杂度:

    最坏情况下,每个字符都可能分割产生 (2^{n}) 种组合,且每次判断回文消耗 (O(n))。

    预计复杂度为 O(n × 2ⁿ)

  • 空间复杂度:

    递归深度最多为 (n),需要保存路径,复杂度为 O(n)


3.4 Java代码实现(基础版)

java 复制代码
import java.util.*;

public class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> res = new ArrayList<>();
        dfs(s, 0, new ArrayList<>(), res);
        return res;
    }

    private void dfs(String s, int start, List<String> path, List<List<String>> res) {
        if (start == s.length()) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = start; i < s.length(); i++) {
            if (isPalindrome(s, start, i)) {
                path.add(s.substring(start, i + 1));
                dfs(s, i + 1, path, res);
                path.remove(path.size() - 1); // 回溯
            }
        }
    }

    private boolean isPalindrome(String s, int l, int r) {
        while (l < r) {
            if (s.charAt(l++) != s.charAt(r--)) return false;
        }
        return true;
    }
}

四、解法二:预处理回文表(动态规划优化)

4.1 优化思路

上面的方法中,每次都重复判断回文。

我们可以提前用一个二维数组 dp[i][j] 记录:

s[i..j] 是回文时,dp[i][j]=true

状态转移:

d p i j = ( s i = = s j ) ∧ ( j − i < 3 或 d p i + 1 j − 1 ) dpij = (si == sj) \land (j - i < 3 \text{ 或 } dpi+1j-1) dpij=(si==sj)∧(j−i<3 或 dpi+1j−1)

这样,判断某个子串是否为回文只需 (O(1))。


4.2 预处理 + 回溯流程图

dpij==true
预处理 dp 数组
递归回溯 dfs
判断 si..j 是否回文
加入方案
返回结果


4.3 时间空间复杂度分析

  • 预处理:遍历全部子串,复杂度 (O(n^2))。
  • 回溯仍然产生组合数 (O(2^n))。

总体复杂度:

时间:约 (O(n^2 + 2^n))

空间:(O(n^2))


4.4 Java代码实现(优化版)

java 复制代码
import java.util.*;

public class Solution {
    public List<List<String>> partition(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n];

        // 预处理所有回文子串情况
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (s.charAt(i) == s.charAt(j) &&
                    (j - i < 3 || dp[i + 1][j - 1])) {
                    dp[i][j] = true;
                }
            }
        }

        List<List<String>> res = new ArrayList<>();
        dfs(s, 0, new ArrayList<>(), res, dp);
        return res;
    }

    private void dfs(String s, int start, List<String> path, List<List<String>> res, boolean[][] dp) {
        if (start == s.length()) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = start; i < s.length(); i++) {
            if (dp[start][i]) {
                path.add(s.substring(start, i + 1));
                dfs(s, i + 1, path, res, dp);
                path.remove(path.size() - 1);
            }
        }
    }
}

五、总结与对比

解法 回文判断方式 时间复杂度 空间复杂度 适用场景
回溯基础版 实时双指针判断 O(n × 2ⁿ) O(n) 字符串较短时
DP优化版 预处理回文表 O(n² + 2ⁿ) O(n²) 大字符串场景
相关推荐
米尔的可达鸭11 分钟前
深入操作系统 Socket 底层:套接字控制块、FD映射、阻塞IO核心完整实现
arm开发·数据结构·websocket·网络协议·算法·架构·安全架构
tachibana220 分钟前
hot100 课程表(207)
java·数据结构·算法·leetcode
神仙别闹21 分钟前
编写基于C++ Huffman 算法的无损压缩程序和解压程序
java·c++·算法
大鱼>35 分钟前
宠物活动轨迹追踪系统:GPS/BDS+UWB+BLE多定位融合方案
人工智能·深度学习·算法·iot·宠物
旖-旎1 小时前
《LeetCode 646 最长数对链 || LeetCode 1143 最长公共子序列》
c++·算法·leetcode·动态规划
Frostnova丶1 小时前
(15)LeetCode 189. 轮转数组
数据结构·算法·leetcode
Lumos1862 小时前
故障保护与鲁棒性(十七)
算法
手写码匠2 小时前
从零实现大模型推理引擎:Continuous Batching 与动态调度系统深度解析
人工智能·深度学习·算法·aigc
wabs6662 小时前
关于单调栈【力扣503.下一个更大元素II的思考】
算法·单调栈
c238562 小时前
下篇:伸缩魔法!进阶吃透可变滑动窗口篇
c++·算法