欢迎光临
我们一直在努力

131. 分割回文串

131. 分割回文串

中等

给你一个字符串 s,请你将 s 分割成一些 子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

示例 1:

输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例 2:

输入:s = "a"
输出:[["a"]]

提示:

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

📝 核心笔记:分割回文串 (选或不选 / 逗号切割法)

1. 核心思想 (一句话总结)

“手拿菜刀走到底:每个字符后面,切一刀还是连着走?”

我们遍历字符串的每一个字符索引 i,对于 i 和 i+1 之间的缝隙,我们面临两个选择:

  • 不切 (Join):当前子串还没结束,继续往后看下一位。
  • 切 (Cut):当前子串 [start, i] 结束。前提是:切下来的这段必须是回文串。
  • 💡 图像记忆 (切香肠):

    • 你有一根香肠 aab。
    • 走到第一个 a 后面:
      • 不切:等着凑个更大的(比如 aa)。
      • 切:切下来一块 a(它是回文,合法),然后处理剩下那截。
    2. 算法流程 (Input View)

    这也是典型的 选或不选 模型(Binary Tree):

  • Base Case:i == n,说明整个字符串处理完了,收集结果。
  • 分支一:不切 (Skip):
      • 条件:i < n – 1。因为最后一个字符后面必须完结(不能悬在半空),所以只有不是最后一个字符时,才能选择“不切”。
      • 动作:dfs(i + 1, start)。start 不变,子串变长了。
  • 分支二:切 (Pick):
      • 条件:isPalindrome(start, i)。只有当前是回文才能切。
      • 动作:记录子串,dfs(i + 1, i + 1)。下一个子串从 i+1 开始。
      • 回溯:removeLast。
    🔍 代码回忆清单 (带注释版)

    // 题目:LC 131. Palindrome Partitioning
    class Solution {
    public List<List<String>> partition(String s) {
    List<List<String>> ans = new ArrayList<>();
    List<String> path = new ArrayList<>();
    dfs(0, 0, s, path, ans);
    return ans;
    }

    // i: 当前正在考虑第 i 个字符 (作为潜在的结尾)
    // start: 当前子串的起始位置
    private void dfs(int i, int start, String s, List<String> path, List<List<String>> ans) {
    // 1. Base Case: 走到字符串末尾
    if (i == s.length()) {
    ans.add(new ArrayList<>(path)); // 复制结果
    return;
    }

    // 2. 分支 A: 不切 (Don't Split)
    // 只有不到最后一位时,才能选择"不切"继续连
    // 如果到了最后一位(n-1),必须切,否则字符串就没结束
    if (i < s.length() – 1) {
    // i 往前移,但 start 不动 (子串在变长)
    dfs(i + 1, start, s, path, ans);
    }

    // 3. 分支 B: 切 (Split)
    // 只有 [start, i] 是回文串时,才有资格切这一刀
    if (isPalindrome(s, start, i)) {
    path.add(s.substring(start, i + 1));
    // i 往前移,start 也移到 i+1 (新子串的起点)
    dfs(i + 1, i + 1, s, path, ans);
    path.removeLast(); // 回溯
    }
    }

    // 标准双指针判断回文
    private boolean isPalindrome(String s, int left, int right) {
    while (left < right) {
    if (s.charAt(left++) != s.charAt(right–)) {
    return false;
    }
    }
    return true;
    }
    }

    ⚡ 快速复习 CheckList (易错点)
    • [ ] 对比标准解法 (For循环枚举)?
      • 你的解法 (二叉树):每个缝隙选/不选。递归深度 。
      • 标准解法 (N叉树):站在 start,枚举终点 j (从 start 到 n)。
      • 结论:你的解法逻辑严密,但在 Java 中因为 substring 的存在,效率相差不大。面试中两种皆可,标准解法代码略短一些。
    • [ ] 边界条件 i < n – 1?
      • 这是此解法的命门。
      • 如果 i 到了最后一个字符,你不能选择“不切”,因为那样会导致递归结束时,最后一段 substring 没被加入 path,字符串被“吞”了一截。
    • [ ] 回文判断优化?
      • 目前 isPalindrome 是 。整个算法接近 。
      • 如果字符串很长,可以用 动态规划 (DP) 预处理一个 boolean[][] isPal 表,把判断降为 。
    🖼️ 数字演练

    输入 s = "aab"

  • DFS(0, 0): 指针在第一个 'a'。
      • 不切: DFS(1, 0) -> start=0, i=1 (当前看着 "aa")。
      • 切: "a" 是回文 -> path=["a"] -> DFS(1, 1)。
  • 分支:DFS(1, 0) (刚才选择了不切,现在看第二个 'a')
      • 不切: DFS(2, 0) -> start=0, i=2 (当前看着 "aab")。
      • 切: "aa" 是回文 -> path=["aa"] -> DFS(2, 2) -> 剩下 "b" -> … -> ["aa", "b"]。
  • 分支:DFS(1, 1) (刚才切了第一个 'a',start=1)
      • 不切: DFS(2, 1) -> start=1, i=2 (当前看着 "ab")。
      • 切: "a" 是回文 -> path=["a", "a"] -> DFS(2, 2) -> … -> ["a", "a", "b"]。

    (最终结果:["aa", "b"], ["a", "a", "b"])

    赞(0)
    未经允许不得转载:171主机测评 » 131. 分割回文串
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址