Day16:字符串的排列
某店铺将用于组成套餐的商品记作字符串 goods
,其中 goods[i]
表示对应商品。请返回该套餐内所含商品的 全部排列方式 。
返回结果 无顺序要求,但不能含有重复的元素。
示例 1:
输入:goods = "agew" 输出:["aegw","aewg","agew","agwe","aweg","awge","eagw","eawg","egaw","egwa","ewag","ewga","gaew","gawe","geaw","gewa","gwae","gwea","waeg","wage","weag","wega","wgae","wgea"]
LCR 157. 套餐内商品的排列顺序 - 力扣(LeetCode)
很经典的回溯题目
我们回顾一下回溯三部曲:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
第一次写的时候忘记去掉重复元素了,但是回溯基本上是对的,没有加剪枝:
class Solution {
public String[] goodsOrder(String goods) {
if(goods == null){
return new String[0];
}
ArrayList<String> results = new ArrayList<>();
StringBuilder result = new StringBuilder();
backtracing(result,goods,results);
return results.toArray(new String[0]);
}
public void backtracing(StringBuilder result, String goods, ArrayList<String> results){
if(result.length() == goods.length()){
results.add(result.toString());
return;
}
for(int i = 0; i < goods.length(); i++){
result = result.append(goods.charAt(i));
backtracing(result,goods,results);
result = result.deleteCharAt(result.length() - 1);
}
}
}
使用HashSet,并使用used数组记录该元素是否被使用过:
class Solution {
public String[] goodsOrder(String goods) {
if(goods == null){
return new String[0];
}
HashSet<String> results = new HashSet<>();
StringBuilder result = new StringBuilder();
boolean[] used = new boolean[goods.length()]; // 记录字符是否被使用过
backtracing(result,goods,results,used);
return results.toArray(new String[0]);
}
public void backtracing(StringBuilder result, String goods, HashSet<String> results,boolean[] used){
if(result.length() == goods.length()){
results.add(result.toString());
return;
}
for(int i = 0; i < goods.length(); i++){
if (used[i]) {
continue;
}
used[i] = true;
result = result.append(goods.charAt(i));
backtracing(result,goods,results,used);
result = result.deleteCharAt(result.length() - 1);
used[i] = false;
}
}
}
其实还有一个问题,像刚才上面的算法,如果是abb这样的字符串,其实是会产生两个abb的,但是我们用HashSet过滤掉了。但是这样会导致算法性能不太好。应该加上这样一个条件判断更合适:
if (i > 0 && goods.charAt(i) == goods.charAt(i - 1) && !used[i - 1]) {
continue;
}
如果当前字符等于上一个字符并且上一个字符没有被使用过,那么就跳过。
但是好像在力扣里加不加这个算法性能没太大区别。