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 标准模板:做选择 -> 递归 -> 撤销选择。

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

相关推荐
hhzz2 分钟前
【OpenCV 入门到精通 04】视频入门与绘图交互:从摄像头到鼠标画笔
人工智能·python·opencv·计算机视觉·音视频·交互
Thomas.Sir2 分钟前
第40课:TensorFlow|模型推理部署前置知识【离线推理、接口调用基础】
人工智能·python·tensorflow
2601_962387824 分钟前
多源数据查询指南:MySQL 联邦、Python 聚合与 Presto 引擎全对比
python·mysql·数据集成·presto·联邦查询
海盗12345 分钟前
DSH 接入 OpenCode Go 报 400 MissingSessionID:从 LLM 语义层退到 fetch 传输层的完整排障
开发语言·后端·golang
weixin1997010801611 分钟前
《Mercari OTA 直连接入:日本二手电商出海的API通道与600次/分钟限流实战》(附Python源码)
开发语言·数据库·python
库玛西15 分钟前
深度解构高并发利器:纯事件驱动 Reactor 模式的设计哲学与工程实践
服务器·开发语言·c++·笔记·tcp/ip
lemon_sjdk26 分钟前
JavaScript 常用关键字全解析
开发语言·javascript·ecmascript
linx29529 分钟前
单元五 · 对称认知·下:编译与链接
linux·c语言·开发语言·c++·嵌入式硬件
半亩码田41 分钟前
【.NET新特性·第17篇】EF Core 10 与 Aspire 13.1
python·flask·.net
zhangzeyuaaa1 小时前
AI Agent 执行引擎选型指南:PowerShell / Bash 和 Python,边界与最佳实践
人工智能·python·bash