给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
首先介绍何为回溯法,如下图所示:
通俗的理解就是通过回溯来遍历所有情况,使用一个数组记录每种情况,且在递归完成后将该数组的内容回溯到,如图中如果目前记录了"ad"的情况,在这种情况枚举完成后,将数组中的“d“删除使数组中元素重新为“a”即是一种回溯,这样可以继续往数组中添加“e”;
当“a”的所有情况枚举完后,将数组中的“a”也删除回溯到数组为空,枚举第一个元素为“b”的情况。这样不断回溯,不断记录,就会枚举完所有的情况。
因此对与本题我们采用回溯法,用path记录当前枚举的情况,ans记录答案。
MAPPING = "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
n=len(digits)
if n == 0:
return []
ans=[]
path=[]
def dfs(i:int)->None:
if i==n:
ans.append("".join(path))
return
for c in MAPPING[int(digits[i])]:
path.append(c)
dfs(i + 1)
path.pop()
dfs(0)
return ans
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
子集型问题分两种思路,一种是从输入的思路来考虑,如本题考虑当前元素是选还是不选,第二种是从答案的思路来考虑,如考虑选哪个元素
采用回溯法,用path记录当前数字选择的情况,ans记录答案。
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ans=[]
path=[]
n=len(nums)
def dfs(i:int)->None:
if i==n:
ans.append(path.copy())# 固定答案
return
#不选 nums[i]
dfs(i + 1)
# 选 nums[i]
path.append(nums[i])
dfs(i + 1)
path.pop() #恢复现场
dfs(0)
return ans
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ans=[]
path=[]
n=len(nums)
def dfs(i:int)->None:
ans.append(path.copy())# 固定答案
if i==n:
return
for j in range(i, n): # 枚举选择的数字
path.append(nums[j])
dfs(j + 1)/#修改的值在当前dfs(j+1)内加入ans中,如本题即先选1
path.pop() #恢复现场,恢复现场后的值将在下一次循环j的dfs(j+1)内加入ans中,即在下一次dfs时没有选择1,选择了2
dfs(0)
return ans
虽然有两种思路,但可以看到仅仅在dfs递归函数中有代码修改,且一般退出递归的条件是一样的,只是在ans更新与何时递归有所不同
给定一个字符串 s ,通过将字符串 s 中的每个字母转变大小写,我们可以获得一个新的字符串。返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。
采用回溯法,用cur记录当前转变大小写的情况,ans记录答案。
func letterCasePermutation(s string) []string {
n:=len(s)
ans:=[]string{}
cur:=[]byte(s)
var dfs func(i int)
dfs=func(i int){
for i<n&&cur[i]>='0'&&cur[i]<='9'{
i++
}
if i==n{
ans=append(ans,string(cur))
return
}
//不转变
dfs(i+1)
//转变
cur[i] ^= 32//异或 32 可进行字母大小写转换
dfs(i+1)
cur[i] ^= 32
}
dfs(0)
return ans
}
func letterCasePermutation(s string) []string {
n:=len(s)
ans:=[]string{}
cur:=[]byte(s)
var dfs func(i int)
dfs=func(i int){
ans=append(ans,string(cur))//每次枚举都是答案
for j:=i;j<n;j++{
if cur[j]>='0'&&cur[j]<='9'{
continue
}
cur[j] ^= 32
dfs(j+1)
cur[j] ^= 32
}
}
dfs(0)
return ans
}
给你一个下标从 0 开始的 m x n 二进制矩阵 mat 和一个整数 cols ,表示你需要选出的列数。
如果一行中,所有的 1 都被你选中的列所覆盖,那么我们称这一行 被覆盖 了。
请你返回在选择 cols 列的情况下,被覆盖 的行数 最大 为多少。
采用回溯法,用selectCols来记录当前选择的列数,用lines函数来计算当前列数选择情况下的覆盖行数,maxAns记录答案。
func maximumRows(matrix [][]int, numSelect int) int {
maxAns:=-1
n:=len(matrix[0])
selectCols:=[13]int{}
var dfs func(i int)
dfs=func(i int){
if numSelect==0{
maxAns=max(maxAns,lines(matrix,selectCols))
return
}else if i==n{
return
}
//不选该列
dfs(i+1)
//选该列
selectCols[i]=1
numSelect--
dfs(i+1)
selectCols[i]=0
numSelect++
}
dfs(0)
return maxAns
}
func lines(matrix [][]int,selectCols [13]int) int{
ans:=0
m:=len(matrix)
n:=len(matrix[0])
for i:=0;i<m;i++{
j:=0
for ;j<n;j++{
if matrix[i][j]==1{
if selectCols[j]==1{continue}
break
}
}
if j==n{
ans++
}
}
return ans
}
func max(a,b int) int{if a>b{return a};return b}
采用回溯法,用selectCols来记录当前选择的列数,用lines函数来计算当前列数选择情况下的覆盖行数,maxAns记录答案。
func maximumRows(matrix [][]int, numSelect int) int {
maxAns:=-1
n:=len(matrix[0])
selectCols:=[13]int{}
var dfs func(i int)
dfs=func(i int){
if numSelect==0{
maxAns=max(maxAns,lines(matrix,selectCols))
return
}else if i==n{
return
}
for j:=i;j<n;j++{
selectCols[j]=1
numSelect--
dfs(j+1)
selectCols[j]=0
numSelect++
}
}
dfs(0)
return maxAns
}
func lines(matrix [][]int,selectCols [13]int) int{
ans:=0
m:=len(matrix)
n:=len(matrix[0])
for i:=0;i<m;i++{
j:=0
for ;j<n;j++{
if matrix[i][j]==1{
if selectCols[j]==1{continue}
break
}
}
if j==n{
ans++
}
}
return ans
}
func max(a,b int) int{if a>b{return a};return b}
我们有 n 栋楼,编号从 0 到 n - 1 。每栋楼有若干员工。由于现在是换楼的季节,部分员工想要换一栋楼居住。
给你一个数组 requests ,其中 requests[i] = [fromi, toi] ,表示一个员工请求从编号为 fromi 的楼搬到编号为 toi 的楼。
一开始 所有楼都是满的,所以从请求列表中选出的若干个请求是可行的需要满足 每栋楼员工净变化为 0 。意思是每栋楼 离开 的员工数目 等于 该楼 搬入 的员工数数目。比方说 n = 3 且两个员工要离开楼 0 ,一个员工要离开楼 1 ,一个员工要离开楼 2 ,如果该请求列表可行,应该要有两个员工搬入楼 0 ,一个员工搬入楼 1 ,一个员工搬入楼 2 。
请你从原请求列表中选出若干个请求,使得它们是一个可行的请求列表,并返回所有可行列表中最大请求数目。1 <= n <= 20,,1 <= requests.length <= 16 ,requests[i].length == 2 ,0 <= fromi, toi < n
采用回溯法,用m记录reques的长度,selects来记录当前选择的换楼请求,用isSatisfy函数来判断当前请求选择情况下是否符合要求,maxAns记录答案。
func maximumRequests(n int, requests [][]int) int {
m:=len(requests)
selects:=make([][]int,0)
maxAns:=-1
var dfs func(int)
dfs=func(i int) {
if i==m{
if isSatisfy(selects){
maxAns=max(maxAns,len(selects))
}
return
}
//不选
dfs(i+1)
//选
selects=append(selects,append([]int{},requests[i]...))
dfs(i+1)
selects=selects[:len(selects)-1]
}
dfs(0)
return maxAns
}
func max(a,b int)int{if(a>b){return a};return b}
func isSatisfy(selects [][]int) bool {
cntFrom:=[21]int{}
cntTo:=[21]int{}
for _,request:=range selects{
cntFrom[request[0]]++
cntTo[request[1]]++
}
for i:=0;i<21;i++{
if cntFrom[i]!=cntTo[i]{
return false
}
}
return true
}
采用回溯法,用m记录reques的长度,selects来记录当前选择的换楼请求,用isSatisfy函数来判断当前请求选择情况下是否符合要求,maxAns记录答案。
func maximumRequests(n int, requests [][]int) int {
m:=len(requests)
selects:=make([][]int,0)
maxAns:=-1
var dfs func(int)
dfs=func(i int) {
if isSatisfy(selects){
maxAns=max(maxAns,len(selects))
}
if i==m{
return
}
for j:=i;j<m;j++{
selects=append(selects,append([]int{},requests[j]...))
dfs(j+1)
selects=selects[:len(selects)-1]
}
}
dfs(0)
return maxAns
}
func max(a,b int)int{if(a>b){return a};return b}
func isSatisfy(selects [][]int) bool {
cntFrom:=[21]int{}
cntTo:=[21]int{}
for _,request:=range selects{
cntFrom[request[0]]++
cntTo[request[1]]++
}
for i:=0;i<21;i++{
if cntFrom[i]!=cntTo[i]{
return false
}
}
return true
}
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。
采用回溯法,用path记录当前分割情况,ans记录答案。
func partition(s string) (ans [][]string) {
path:=[]string{}
n:=len(s)
var dfs func( int,int )
dfs=func(i ,start int ){
if i==n{
ans=append(ans,append([]string{},path...))
return
}
// 不选 i 和 i+1 之间的逗号(i=n-1 时已到字符串末尾,必须进行分割)
if i < n-1 {
dfs(i+1,start)
}
// 选 i 和 i+1 之间的逗号
if isPalindrome(s,start,i){
path=append(path,s[start:i+1])
dfs(i+1,i+1)
path = path[:len(path)-1] // 恢复现场
}
}
dfs(0,0)
return
}
func isPalindrome(s string, left, right int) bool {
for left < right {
if s[left] != s[right] {
return false
}
left++
right--
}
return true
}
采用回溯法,用path记录当前枚举的情况,ans记录答案。
func partition(s string) (ans [][]string) {
path:=[]string{}
n:=len(s)
var dfs func( int)
dfs=func(i int){
if i==n{
ans=append(ans,append([]string{},path...))
return
}
for j:=i;j<n;j++{// 枚举子串的结束位置
if isPalindrome(s,i,j){
path=append(path,s[i:j+1])
dfs(j+1)
path=path[:len(path)-1]
}
}
}
dfs(0)
return
}
func isPalindrome(s string, left, right int) bool {
for left < right {
if s[left] != s[right] {
return false
}
left++
right--
}
return true
}