描述
有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。
示例1:
输入:S = "qqe" 输出:["eqq","qeq","qqe"]
示例2:
输入:S = "ab" 输出:["ab", "ba"]
提示:
- 字符都是英文字母。
- 字符串长度在[1, 9]之间。
使用特定函数
from itertools import permutations # 导入 permutations 函数
from typing import List # 导入 List 类型
class Solution:
def permutation(self, S: str) -> List[str]:
# 生成字符串 S 的所有排列,并使用 set 去重
lis = set(permutations(S))
# 将每个排列转换为字符串,并存入列表中
return ["".join(p) for p in lis]
排列组合的应用(itertools库中的函数)
全排列函数permutations()
result=itertools.permutations(iterable,r)
其中result为对迭代对象处理之后返回的结果,数据类型为'itertools.permutations',如果需要的话,可以通过list()转化为列表。转化为列表之后的元素的数据类型为元组。元素默认排列顺序为迭代对象字典序上的从小到大(自行体会:>);
iterable为需要排列的迭代对象,包括列表、字符串、元组、字典(只对键进行全排列);
r为单个排列元素的长度,不修改的话,默认为迭代对象的元素个数。
全组合函数combinations()
result=itertools.combinations(iterable,r)
其中result为迭代对象处理之后返回的结果,数据类型为'itertools.combinations',如果需要的话,可以通过list()转化为列表。转化为列表之后的元素的数据类型为元组。元素默认排列顺序为按照迭代对象字典序上的从小到大;
iterable为需要排列的迭代对象,包括列表、字符串、元组、字典(只对键进行全组合);
r为单个排列元素的长度,必须修改,不然会报错。
回溯法
from typing import List # 导入 List 类型
class Solution:
def permutation(self, S: str) -> List[str]:
S = sorted(S) # 将字符串 S 转换为排序后的列表,以便处理重复字符
def backtrace(index):
if len(path) == len(S): # 如果当前路径长度等于 S 的长度
res.append("".join(path)) # 将路径转换为字符串并添加到结果中
return
for i in range(len(S)): # 遍历 S 中的每个字符
if S[i] == 0: # 如果字符已经被使用过,跳过
continue
if i > 0 and S[i] == S[i-1] and S[i-1] != 0: # 跳过重复字符
continue
path.append(S[i]) # 将字符添加到路径中
temp = S[i] # 保存当前字符
S[i] = 0 # 标记字符为已使用
backtrace(index + 1) # 递归调用
S[i] = temp # 恢复字符
path.pop() # 移除路径中的最后一个字符
path = [] # 存储当前路径
res = [] # 存储结果
backtrace(0) # 开始回溯
return res # 返回结果
最强回溯算法总结
理论基础
回溯是递归的副产品,只要有递归就会有回溯,所以回溯法也经常和二叉树遍历,深度优先搜索混在一起,因为这两种方式都是用了递归。
回溯法就是暴力搜索,并不是什么高效的算法,最多再剪枝一下。
回溯算法能解决如下问题:
组合问题:N个数里面按一定规则找出k个数的集合
排列问题:N个数按一定规则全排列,有几种排列方式
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
棋盘问题:N皇后,解数独等等
算法模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}