有一个考古学家发现一个石碑,但是很可惜 发现时其已经断成多段,原地发现 N
个断口整齐的石碑碎片,为了破解石碑内容
考古学家希望有程序能帮忙计算复原后的石碑文字,你能帮忙吗
如果存在石碑碎片内容完全相同,则由于碎片间的顺序不影响复原后的碑文内容, 仅相同碎片间的位置变化不影响组合
第一行输入 N
,N
表示石碑碎片的个数 第二行依次输入石碑碎片上的文字内容 S
共有 N
组
输出石碑文字的组合(按照升序排列),行尾无多余空格
3
a b c
abc
acb
bac
bca
cab
cba
3
a b a
aab
aba
baa
3
a b ab
aabb
abab
abba
baab
baba
本题属于排列回溯问题的板子题,和LeetCode47. 全排列II几乎完全一致。
唯一需要注意的是去重,需要多加一个ans_set
哈希表来完成去重操作。
# 题目:2023B-考古学家
# 分值:200
# 作者:许老师-闭着眼睛学数理化
# 算法:回溯
# 代码看不懂的地方,请直接在群上提问
# 回溯函数
# words: 单词数组
# ans: 答案数组
# ans_set: 用于判断是否出现重复答案的集合
# path: 当前回溯的路径数组
# used: 判断某元素是否使用过的bool型数组
def dfs(words, ans, ans_set, path, used):
# 递归终止条件:
# 路径长度等于单词数组长度,说明所有单词均被使用
if len(path) == len(words):
path_str = "".join(path)
# 若此时路径在此之前没出现过,则加入ans和ans_set中
if path_str not in ans_set:
ans.append(path_str)
ans_set.add(path_str)
return
# 横向遍历所有单词
for i in range(len(words)):
# 如果单词words[i]已经被使用过,或者和上一个单词相等,则无需再进行回溯,直接跳过
if used[i] or (i > 0 and words[i] == words[i - 1] and not used[i - 1]):
continue
# 状态更新
used[i] = True
path.append(words[i])
# 回溯
dfs(words, ans, ans_set, path, used)
# 回滚
used[i] = False
path.pop()
# 输入数组长度,单词数组
n = int(input())
words = input().split()
# 对words进行从小到大排列
# 这样才能保证组合出来每一个密码都是按升序排序
words.sort()
ans = list()
used = [False] * n
# 递归调用入口
dfs(words, ans, set(), [], used)
# 逐行输出答案
for res in ans:
print(res)
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
scanner.nextLine();
String[] words = scanner.nextLine().split(" ");
Arrays.sort(words); // 对单词数组排序
List<String> ans = new ArrayList<>();
boolean[] used = new boolean[n];
dfs(words, ans, new HashSet<>(), new ArrayList<>(), used);
for (String res : ans) {
System.out.println(res);
}
}
public static void dfs(String[] words, List<String> ans, Set<String> ansSet, List<String> path, boolean[] used) {
if (path.size() == words.length) {
String pathStr = String.join("", path);
if (!ansSet.contains(pathStr)) {
ans.add(pathStr);
ansSet.add(pathStr);
}
return;
}
for (int i = 0; i < words.length; i++) {
if (used[i] || (i > 0 && words[i].equals(words[i - 1]) && !used[i - 1])) {
continue;
}
used[i] = true;
path.add(words[i]);
dfs(words, ans, ansSet, path, used);
used[i] = false;
path.remove(path.size() - 1);
}
}
}
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
void dfs(const vector<string>& words, vector<string>& ans, set<string>& ansSet, vector<string>& path, vector<bool>& used);
int main() {
int n;
cin >> n;
vector<string> words(n);
for (int i = 0; i < n; i++) {
cin >> words[i];
}
sort(words.begin(), words.end());
vector<string> ans;
set<string> ansSet;
vector<bool> used(n, false);
vector<string> path;
dfs(words, ans, ansSet, path, used);
for (const string& res : ans) {
cout << res << endl;
}
return 0;
}
void dfs(const vector<string>& words, vector<string>& ans, set<string>& ansSet, vector<string>& path, vector<bool>& used) {
if (path.size() == words.size()) {
string pathStr;
for (const string& str : path) {
pathStr += str;
}
if (ansSet.find(pathStr) == ansSet.end()) {
ans.push_back(pathStr);
ansSet.insert(pathStr);
}
return;
}
for (int i = 0; i < words.size(); i++) {
if (used[i] || (i > 0 && words[i] == words[i - 1] && !used[i - 1])) {
continue;
}
used[i] = true;
path.push_back(words[i]);
dfs(words, ans, ansSet, path, used);
used[i] = false;
path.pop_back();
}
}
时间复杂度:O(N!)
。
空间复杂度:O(N)
。
华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!
课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化
每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!
60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁
可上全网独家的欧弟OJ系统练习华子OD、大厂真题
可查看链接 大厂真题汇总 & OD真题汇总(持续更新)
绿色聊天软件戳 od1336
了解更多