题目描述
思路解析动画文字版
记住这条:枚举切点 → 前缀是回文才往下切 → 切到末尾收集一种方案。下面逐帧看它怎么跑。
从下标 0 起试切前缀 "a"(切到下标 0):它正着倒着都一样,是回文 ✓。切下它放进 path → ["a"],再去切剩下的 "abb"。
从下标 1 起试切前缀 "a"(切到下标 1):它正着倒着都一样,是回文 ✓。切下它放进 path → ["a","a"],再去切剩下的 "bb"。
从下标 2 起试切前缀 "b"(切到下标 2):它正着倒着都一样,是回文 ✓。切下它放进 path → ["a","a","b"],再去切剩下的 "b"。
从下标 3 起试切前缀 "b"(切到下标 3):它正着倒着都一样,是回文 ✓。切下它放进 path → ["a","a","b","b"],再去切剩下的 "(空)"。
start 已经走到串尾,全部字符都切完了——当前 path=["a","a","b","b"] 就是一种完整的分割,收进结果(第 1 种)。
"b" 这条分支已经探完,撤销它(path 弹出),回到 ["a","a","b"],让 start 复位去试更长的前缀。
"b" 这条分支已经探完,撤销它(path 弹出),回到 ["a","a"],让 start 复位去试更长的前缀。
从下标 2 起试切前缀 "bb"(切到下标 3):它正着倒着都一样,是回文 ✓。切下它放进 path → ["a","a","bb"],再去切剩下的 "(空)"。
start 已经走到串尾,全部字符都切完了——当前 path=["a","a","bb"] 就是一种完整的分割,收进结果(第 2 种)。
"bb" 这条分支已经探完,撤销它(path 弹出),回到 ["a","a"],让 start 复位去试更长的前缀。
"a" 这条分支已经探完,撤销它(path 弹出),回到 ["a"],让 start 复位去试更长的前缀。
从下标 1 起试切前缀 "ab"(切到下标 2):首尾字符不同,不是回文 ✗。剪掉这条分支——前缀都不合法就没必要再切后面的了。
从下标 1 起试切前缀 "abb"(切到下标 3):首尾字符不同,不是回文 ✗。剪掉这条分支——前缀都不合法就没必要再切后面的了。
"a" 这条分支已经探完,撤销它(path 弹出),回到 [空],让 start 复位去试更长的前缀。
从下标 0 起试切前缀 "aa"(切到下标 1):它正着倒着都一样,是回文 ✓。切下它放进 path → ["aa"],再去切剩下的 "bb"。
从下标 2 起试切前缀 "b"(切到下标 2):它正着倒着都一样,是回文 ✓。切下它放进 path → ["aa","b"],再去切剩下的 "b"。
从下标 3 起试切前缀 "b"(切到下标 3):它正着倒着都一样,是回文 ✓。切下它放进 path → ["aa","b","b"],再去切剩下的 "(空)"。
start 已经走到串尾,全部字符都切完了——当前 path=["aa","b","b"] 就是一种完整的分割,收进结果(第 3 种)。
"b" 这条分支已经探完,撤销它(path 弹出),回到 ["aa","b"],让 start 复位去试更长的前缀。
"b" 这条分支已经探完,撤销它(path 弹出),回到 ["aa"],让 start 复位去试更长的前缀。
从下标 2 起试切前缀 "bb"(切到下标 3):它正着倒着都一样,是回文 ✓。切下它放进 path → ["aa","bb"],再去切剩下的 "(空)"。
start 已经走到串尾,全部字符都切完了——当前 path=["aa","bb"] 就是一种完整的分割,收进结果(第 4 种)。
"bb" 这条分支已经探完,撤销它(path 弹出),回到 ["aa"],让 start 复位去试更长的前缀。
"aa" 这条分支已经探完,撤销它(path 弹出),回到 [空],让 start 复位去试更长的前缀。
从下标 0 起试切前缀 "aab"(切到下标 2):首尾字符不同,不是回文 ✗。剪掉这条分支——前缀都不合法就没必要再切后面的了。
从下标 0 起试切前缀 "aabb"(切到下标 3):首尾字符不同,不是回文 ✗。剪掉这条分支——前缀都不合法就没必要再切后面的了。
边界:单字符必是回文;越多相同字符,合法切法越多。
两个高频追问:回文表预处理 + 最少切割变体。
参考代码
def partition(s): res = [] def isPal(t): return t == t[::-1] # 正反相同即回文 def backtrack(start, path): if start == len(s): res.append(path[:]) # 切到末尾,收集一种分割 return for end in range(start, len(s)): sub = s[start:end + 1] # 试切前缀 s[start..end] if isPal(sub): # 是回文才继续切剩余 path.append(sub) backtrack(end + 1, path) path.pop() # 撤销,回溯 backtrack(0, []) return res复杂度
- 时间:O(n·2ⁿ),n-1 个切点各切或不切共 2ⁿ⁻¹ 种,每种判回文 + 复制 O(n)
- 空间:O(n),递归深度 + path 长度,不计结果存储
易错点
面试追问把动画讲成自己的话
追问每次都重新判回文会不会很慢?怎么优化?
追问如果只问「最少切几刀」(LC132)呢?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
电话号码的字母组合
LeetCode 17 · 中等 · 沿着 回溯 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题