Leetcode131题解 -Python-回溯+cache缓存

这是一个非常经典的**回溯算法(Backtracking)**题目,通常对应 LeetCode 131. Palindrome Partitioning(分割回文串)。


算法题解:分割回文串 (LeetCode 131)

在处理字符串分割问题时,我们经常需要找到满足特定条件(如"必须是回文")的所有分割方案。这类问题是回溯算法的典型应用场景。

今天我们来拆解一种结构非常清晰的解法:预处理 + DFS回溯

核心思路

这个问题的难点在于:我们需要把字符串切成若干块,而且每一块都必须是回文串。

我们的策略分为两步走:

  1. 预处理 (Preprocessing):先算好哪些子串是回文串,存进一个表格(Cache)里。这样在后面搜索时,查表就知道是不是回文,不用重复计算。

  2. 回溯搜索 (Backtracking):利用深度优先搜索(DFS)尝试在每一个位置"切一刀"。如果切出来的这一段是回文,就继续往下切;否则这就不是一条可行的路。


逻辑图解

假设输入字符串 s = "aab"

  1. 预处理阶段 :我们生成一个二维数组 cache

    • s[0:0] ("a") -> True

    • s[1:1] ("a") -> True

    • s[0:1] ("aa") -> True

    • s[2:2] ("b") -> True

    • ... 其他为 False

  2. DFS 搜索树

    Plaintext

    复制代码
               dfs(0) -> 剩余 "aab"
              /          \
       切 "a" (是回文)    切 "aa" (是回文)
       /                  \
    dfs(1) -> 剩 "ab"    dfs(2) -> 剩 "b"
      /                   \
    切 "a" (是回文)       切 "b" (是回文)
    /                      \
    dfs(2) -> 剩 "b"      dfs(3) -> 结束 -> 收集 ["aa", "b"]
    /
    切 "b" (是回文)
    /
    dfs(3) -> 结束 -> 收集 ["a", "a", "b"]

代码详解

1. 基础工具:判断回文 (isPan)

这是一个标准的双指针写法。

Python

复制代码
def isPan(self, s):
    left, right = 0, len(s)-1
    while left <= right:
        if s[left] != s[right]:
            return False
        left += 1
        right -= 1
    return True

📝 :虽然这部分很简单,但被频繁调用。

2. 核心逻辑:partition 主函数

第一步:建立缓存 (Cache)

为了避免在 DFS 过程中反复检查同一个子串是不是回文,代码先暴力计算了所有可能的子串。

Python

复制代码
# 初始化全为 False 的二维数组
cache = [[False for _ in range (len(s))] for _ in range (len(s))]

# 填表:检查从 i 到 j 的子串 s[i:j+1] 是否为回文
for i in range(len(s)):
    for j in range(i, len(s)):
        if self.isPan(s[i:j+1]):
            cache[i][j] = True
第二步:DFS 回溯

这是算法的灵魂。我们需要维护两个变量:

  • k: 当前处理到的字符串起始索引。

  • path: 当前已经切分好的回文串列表。

Python

复制代码
res, path = [], []

def dfs(k):
    # 🛑 终止条件:如果我们已经处理到了字符串末尾
    if k >= len(s):
        res.append(path.copy()) # 注意要拷贝一份 path,因为 path 是引用的
        return

    # 🔄 单层搜索逻辑:尝试从 k 切到 i
    for i in range(k, len(s)):
        # 查表:如果 s[k:i+1] 是回文
        if cache[k][i]:
            path.append(s[k:i+1])   # 1. 做选择:加入路径
            dfs(i+1)                # 2. 递归:处理剩下的部分
            path.pop()              # 3. 撤销选择:回溯,尝试下一种切法

复杂度分析

  • 时间复杂度

    • 预处理部分 :双重循环遍历所有子串是 n^2,内部调用的 isPan 是 O(N),所以预处理是 O(N^2)。
  • 空间复杂度

    • cache 数组占用 O(N^2)。

    • 递归调用栈最大深度为 O(N)。


总结

这段代码通过空间换时间 的思想(使用 cache),让 DFS 过程中的判断变得非常快(O(1))。虽然预处理部分还有优化的空间(可以使用中心扩展法或动态规划将预处理降为 O(N^2)),但作为一道面试题解,这种结构化、模块化的写法非常利于展示思路。

关键点回顾:

  1. 双指针判断回文。

  2. 二维数组缓存结果。

  3. Backtracking 标准模板:做选择 -> 递归 -> 撤销选择。

希望这篇解析能帮你搞定分割回文串!

相关推荐
SunnyDays101118 小时前
如何使用 Python 合并多个 Excel 文件
python·合并excel文件·合并excel表格
lixzest18 小时前
PyTorch张量(Tensor)简介
python
山上三树18 小时前
详细介绍 C 语言中的匿名结构体
c语言·开发语言·算法
亮子AI18 小时前
注册成功的提示信息怎么写?
数据库·python
大猫和小黄18 小时前
Java开发过程中的各种ID生成策略
java·开发语言·id
繁依Fanyi19 小时前
从初识到实战 | OpenTeleDB 安装迁移使用指南
开发语言·数据库·python
小罗和阿泽19 小时前
java [多线程基础 二】
java·开发语言·jvm
小罗和阿泽19 小时前
java 【多线程基础 一】线程概念
java·开发语言·jvm
Kratzdisteln19 小时前
【MVCD 1】
python