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

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

相关推荐
昵称画7 小时前
交接班记录电子化:三班之间的信息不漏
开发语言·数据库·人工智能·低代码·php
“AI国潮设计-小江”7 小时前
【AIGC实战】Python+SDXL批量生成“潮汕英歌舞不倒翁”IP | 角色一致性与多场景融合方案,附完整代码
开发语言·人工智能·python·prompt·aigc
传奇开心果编程7 小时前
【Rust入门练中学】第6课:结构体与方法
开发语言·rust·结构体·编程语言理论·结构体方法·编程通用语言
Linux_cy7 小时前
服务器管理工具哪个好用?我用了七八个
运维·服务器·javascript·python·ssh·面板·gmssh
MoRanzhi12037 小时前
第6篇(下)· 使用 PyQt5 实现 2048 高性能计算版:统一运行控制与跨模式安全交接
python·pyqt·进程管理·状态机·软件架构·并发控制·数据一致性
code tsunami7 小时前
【无标题】
开发语言·python
CodeGL8 小时前
10、Python基础之进程和线程
开发语言·python
TechWayfarer9 小时前
PHP漏洞被利用,怎么揪出攻击IP?
开发语言·python·tcp/ip·网络安全·php
2601_9679801110 小时前
Python列表操作方法详解
python·数据类型·修改·列表操作·功能汇总
卡卡~~10 小时前
“101计划”核心课程实践教材《数据实验结构指导-C++语言版》题目集
开发语言·c++·学习·算法