解一:倒序排列candidates数组。对当前元素cur,求在不超出target的前提下最多出现次数rep,遍历所有可能的出现次数c;对当前c,将c个cur放入path,递归调用backtrace并传入cur的下一个元素和target-cur*c,每次调用backtrace都回溯path(target的回溯隐含在参数传递中)。递归退出条件是遍历完整个数组或达到target==0。由于对于每个元素,都可以选择c=0,即不将它放入path,隐含了对start在len(candidates)上的遍历,因此不需要再遍历range(start, len),否则会造成重复。
class Solution:
def __init__(self):
self.path = []
self.res = []
def backtrace(self, start, candidates, target):
if target == 0:
self.res.append(self.path[:])
return
if start >= len(candidates):
return
cur = candidates[start]
rep = target // cur #最大可重复次数
for c in range(rep, -1, -1): #cur可以出现rep, ..., 1, 0次
self.path += [cur] * c
self.backtrace(start+1, candidates, target-c*cur)
for _ in range(c):
self.path.pop()
def combinationSum(self, candidates, target):
candidates.sort()
candidates.reverse()
self.backtrace(0, candidates, target)
return self.res
解二:排序数组candidates后,遍历[start, len),若当前元素candidates[i]不超过target,加入path,递归调用traceback并传入i和target-candidates[i],随后回溯。这里通过传入i(当前元素下标)而不是i+1(下一个元素下标)允许了数字重复。
class Solution:
def __init__(self):
self.path = []
self.res = []
def backtrace(self, start, candidates, target):
if target == 0:
self.res.append(self.path[:])
return
for i in range(start, len(candidates)):
if candidates[i] <= target:
self.path.append(candidates[i])
self.backtrace(i, candidates, target-candidates[i])
self.path.pop()
def combinationSum(self, candidates, target):
candidates.sort()
self.backtrace(0, candidates, target)
return self.res
难点是去重操作。尝试:在target==0、res.append之前加一个判断:if self.path in self.res,但是会超时。
在搜索答案的时候,首先确定path的第一个元素(对应树的第一层),再次调用traceback,进入树的下一层,也就是path下一个位置的元素...(以上操作沿着树垂直向下)直到达到递归中止条件,path回退末尾元素(垂直向上),平行向右移动搜索path当前位置的其他可能值,重复。去重是指,在数组已排序的前提下,path同一个位置(或说树的同一层)不可以重复出现一样的元素。
解法:同层的元素共享一个范围 candidates[start : -1],只需比较当前元素是否与同层的上一个(它左边的)元素相等,若相等,为避免重复,跳过此元素,查看下一个元素。
class Solution(object):
def __init__(self):
self.path = []
self.res = []
def traceback(self, candidates, start, target):
if target == 0:
self.res.append(self.path[:])
return
for i in range(start, len(candidates)):
if candidates[i] > target:
break
if i > start and candidates[i-1] == candidates[i]:
#去重:当i不是这一层的首个元素而且与这一层已经访问过的元素相同,就跳过这个i
continue
self.path.append(candidates[i])
self.traceback(candidates, i+1, target-candidates[i])
self.path.pop()
def combinationSum2(self, candidates, target):
candidates.sort() #[1, 1, 2, 5, 6, 7, 10]
self.traceback(candidates, 0, target)
return self.res
class Solution(object):
def __init__(self):
self.res = []
self.path = []
def dp(self, s):
for i in range(len(s)-1, -1, -1):
for j in range(i, len(s)):
if i == j:
self.isval[i][j] = True
elif i+1 == j:
self.isval[i][j] = s[i] == s[j]
else:
self.isval[i][j] = s[i] == s[j] and self.isval[i+1][j-1]
def bt(self, s, start):
if start >= len(s):
self.res.append(self.path[:])
return
for i in range(start, len(s)):
if self.isval[start][i]:
self.path.append(s[start:i+1])
self.bt(s, i+1)
self.path.pop()
def partition(self, s):
self.isval = [[False] * len(s) for _ in range(len(s))]
self.dp(s)
self.bt(s, 0)
return self.res