参考:https://leetcode-cn.com/problems/subsets/solution/c-zong-jie-liao-hui-su-wen-ti-lei-xing-dai-ni-gao-/
1、子集、子集 II、组合、组合总和、组合总和 II
A、78子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
B、90子集 II(剪枝思想)
给定一个可能 包含重复元素 的整数数组 nums,返回该数组所有可能的子集(幂集)。
需要先排序,跳过重复元素
C、77组合
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
D、39组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
result = []
res=[]
def dfs(temp,res):
nonlocal target
if temp > target: # 剪枝一:当前的总值大于目标值
return
if temp == target: # 当前值和目标值相等的时候,保存当前结果,并返回
result.append(res)
return
for i in candidates:
if res and res[-1] > i: # 防止重复的方法是,不让其找在当前元素以前的元素
continue
dfs(temp + i, res + [i])
dfs(0, res)
return result
E、40组合总和 II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
2、全排列、全排列 II、字符串的全排列、字母大小写全排列
子集、组合与排列是不同性质的概念。子集、组合是无关顺序的,而排列是和元素顺序有关的,如 [1,2] 和 [2,1] 是同一个组合(子集),但 [1,2] 和 [2,1] 是两种不一样的排列!!!!因此被分为两类问题
A、46全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
def dfs(path,j):
if(j==len(nums)):
ans.append(path[:]) # 如果有pop语句的话,就必须加上[:],这是为什么呢
return
for i in range(len(nums)):
if nums[i] not in path:
path.append(nums[i])
dfs(path,j+1)
path.pop(-1)
ans=[]
dfs([],0)
# print(ans)
return ans
B、47全排列 II
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
C、剑38字符串的全排列
D、784字母大小写全排列
给定一个字符串S
,通过将字符串S
中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。
E、87. 扰乱字符串(困难)
等价 951. 翻转等价二叉树
class Solution:
@functools.lru_cache(None)
def isScramble(self, s1: str, s2: str) -> bool:
N = len(s1)
if N == 0: return True
if N == 1: return s1 == s2
if sorted(s1) != sorted(s2):
return False
for i in range(1, N):
if self.isScramble(s1[:i], s2[:i]) and self.isScramble(s1[i:], s2[i:]):
return True
elif self.isScramble(s1[:i], s2[-i:]) and self.isScramble(s1[i:], s2[:-i]):
return True
return False
有问题、时间超时,每次输入是不一样的无法缓存
class Solution:
def isScramble(self, s1: str, s2: str) -> bool:
def dfs(i,j,tmp):
if sorted(tmp[i:j]) != sorted(s2[i:j]): # 注意
return False
if tmp[i:j]==s2[i:j]: # 超时
return True
for m in range(i+1,j):
if dfs(i,m,tmp) and dfs(m,j,tmp):
return True
a=tmp[i:j] # 注意
tmp=tmp[0:i]+tmp[m:j]+tmp[i:m]+tmp[j:len(s1)]
if dfs(i,j-m+i,tmp) and dfs(j-m+i,j,tmp): # 注意
return True
tmp=tmp[0:i]+a+tmp[j:len(s1)]
return False
tmp=s1
if dfs(0,len(s1),tmp):
return True
else:
return False
3、解数独、单词搜索、N皇后、分割回文串、二进制手表
A、37解数独
B、79单词搜索
和剑指 Offer 12. 矩阵中的路径题目相同
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
row = len(board)
col = len(board[0])
def helper(i, j, k, visited):
#print(i,j, k,visited)
if k == len(word): # 终止条件
return True
for x, y in [(-1, 0), (1, 0), (0, 1), (0, -1)]: # 选择列表
tmp_i = x + i
tmp_j = y + j
if 0 <= tmp_i < row and 0 <= tmp_j < col and (tmp_i, tmp_j) not in visited \ # 做出选择
and board[tmp_i][tmp_j] == word[k]:
visited.add((tmp_i, tmp_j))
if helper(tmp_i, tmp_j, k+1, visited): # 注意,注意!!
return True
visited.remove((tmp_i, tmp_j)) # 回溯
return False
for i in range(row):
for j in range(col):
if board[i][j] == word[0] and helper(i, j, 1,{(i, j)}) : # {(i, j)}
return True
return False