最朴素的回溯
直接写了一个朴素的回溯,无剪枝也无优化的
class Solution:
def partition(self, s: str) -> List[List[str]]:
self.ans = []
self.back(s,[])
return self.ans
def back(self,s,lst):
if s == '':
self.ans.append(lst.copy())
return
for i in range(len(s)):
# 左半部分
left = s[:i+1]
# 如果是回文
if self.judge(left):
# 添加
lst.append(left)
# 处理右半部分
right = self.back(s[i+1:],lst)
lst.pop()
def judge(self,s):
for i in range(len(s)//2):
if s[i] != s[len(s)-i-1]:
return False
return True
AC了
题解
题解的方法有两种
回溯+动态规划
主要是优化了判断回文的过程,和 5. 最长回文子串 - 力扣(LeetCode) (leetcode-cn.com) 的做法很相像
答案
class Solution:
def partition(self, s: str) -> List[List[str]]:
n = len(s)
f = [[True] * n for _ in range(n)]
# 判断可能存在的回文子串
for i in range(n - 1, -1, -1):
for j in range(i + 1, n):
f[i][j] = (s[i] == s[j]) and f[i + 1][j - 1]
ret = list()
ans = list()
def dfs(i: int):
if i == n:
ret.append(ans[:])
return
for j in range(i, n):
# 避免调用judge重复验证
if f[i][j]:
ans.append(s[i:j+1])
dfs(j + 1)
ans.pop()
dfs(0)
return ret
回溯+记忆化搜索
class Solution:
def partition(self, s: str) -> List[List[str]]:
n = len(s)
ret = list()
ans = list()
@cache
def isPalindrome(i: int, j: int) -> int:
if i >= j:
return 1
return isPalindrome(i + 1, j - 1) if s[i] == s[j] else -1
def dfs(i: int):
if i == n:
ret.append(ans[:])
return
for j in range(i, n):
if isPalindrome(i, j) == 1:
ans.append(s[i:j+1])
dfs(j + 1)
ans.pop()
dfs(0)
isPalindrome.cache_clear()
return ret
方法一中的动态规划预处理计算出了任意的 s[i…j]s[i…j] 是否为回文串,但在实际的搜索过程中,并不是每个 ss 的子串都是会被搜索到的,因此我们可以将这一步改为记忆化搜索,减少算法的常数。
这个@cache
是python 3.9的新功能,加上它之后,计算过程中的结果都保存在缓存中,每次调用函数前先查看缓存,具体见文档:
https://docs.python.org/zh-cn/3/library/functools.html
这段代码相当于优化了判断回文的次数
总结
朴素回溯:
最直观的想法,回溯+O(n)时间判断回文
回溯+DP:
避免O(n)时间判断回文的开销,先DP走一遍,得到所有回文的结果
回溯+记忆化搜索:
在方法二的基础上提升,避免判断所有DP
回溯的时间开销是O(2^n),这是无法避免的