华为OD机试(C卷+D卷)2024真题目录(Java & c++ & python)
题目描述
有一个考古学家发现一个石碑,但是很可惜,发现时其已经断成多段,原地发现n个断口整齐的石碑碎片。
为了破解石碑内容,考古学家希望有程序能帮忙计算复原后的石碑文字组合数,你能帮忙吗?
输入描述
第一行输入 n
n表示石碑碎片的个数
第二行依次输入石碑碎片上的文字内容s,共有n组。
输出描述
输出石碑文字的组合(按照升序排列),行末无多余空格。
备注
如果存在石碑碎片内容完全相同,则由于碎片间的顺序变换不影响复原后的碑文内容,即相同碎片间的位置变换不影响组合。
用例1
输入
3
a b c
输出
abc
acb
bac
bca
cab
cba
说明 当石碑碎片上的内容为“a”,“b”,“c”时,则组合有“abc”,“acb”,“bac”,“bca”,“cab”,“cba”
用例2
输入
3
a b a
输出
aab
aba
baa
说明 当石碑碎片上的内容为“a”,“b”,“a”时,则可能的组合有“aab”,“aba”,“baa”
用例3
输入
3
a b ab
输出
aabb
abab
abba
baab
baba
说明 当石碑碎片上的内容为“a”,“b”,“ab”时,则可能的组合有“aabb”,“abab”,“abba”,“baab”,“baba”
解题思路
全排列问题+去重。
因为会出现重复的结果,所以最后需要去重处理。
-
深度优先搜索 (DFS):
使用 DFS 递归生成所有可能的字符串组合。
通过 used 数组标记哪些字符串已经在当前路径中使用过。
使用 set 来存储结果,并保证输出的唯一性和有序性。 -
去重策略:
首先对输入数组进行排序,方便在 DFS 中跳过重复元素,从而避免生成重复的组合。 -
结果输出:
最终输出所有唯一的组合,按字典序排列。
C++、Java、Python代码如下:
C++参考代码
#include <bits/stdc++.h>
using namespace std;
int n;
vector<string> arr;
// DFS 函数生成所有可能的字符串组合
void dfs(vector<bool> &used, const string &path, int level, set<string> &res) {
// 如果当前路径已经使用了 n 个字符串,将结果存入集合
if (level == n) {
res.insert(path);
return;
}
// 遍历所有字符串,尝试将每个字符串加入当前路径
for (int i = 0; i < n; i++) {
if (used[i]) continue; // 如果当前字符串已经使用过,跳过
// 树层去重:如果当前字符串与前一个字符串相同且前一个字符串未使用,则跳过
if (i > 0 && arr[i] == arr[i - 1] && !used[i - 1]) continue;
// 标记当前字符串为已使用,并递归处理下一个字符串
used[i] = true;
dfs(used, path + arr[i], level + 1, res);
used[i] = false; // 回溯,取消当前字符串的使用标记
}
}
int main() {
cin >> n;
arr.resize(n);
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
// 排序是为了让相同元素相邻,方便后续去重
sort(arr.begin(), arr.end());
vector<bool> used(n, false); // 初始化标记数组
set<string> res; // 用于存储所有唯一组合
dfs(used, "", 0, res);
// 输出石碑文字的组合(按照字典序排列)
for (const auto &item : res) {
cout << item << endl;
}
return 0;
}
Java参考代码
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
static int n;
static String[] arr;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = Integer.parseInt(sc.nextLine());
arr = sc.nextLine().split(" ");
getResult();
}
public static void getResult() {
// 对数组进行排序,使相同元素相邻,方便后续去重
Arrays.sort(arr);
boolean[] used = new boolean[n]; // 用于标记每个字符串是否已使用
LinkedList<String> path = new LinkedList<>(); // 当前路径中的字符串组合
HashSet<String> res = new HashSet<>(); // 用于存储最终的组合结果
// 执行深度优先搜索,生成所有组合
dfs(used, path, res);
// 输出石碑文字的组合,按照字典序排列
res.stream().sorted(String::compareTo).forEach(System.out::println);
}
public static void dfs(boolean[] used, LinkedList<String> path, HashSet<String> res) {
if (path.size() == n) {
// 当路径长度等于n时,生成一个完整的组合,将其加入结果集
StringBuilder sb = new StringBuilder();
path.forEach(sb::append);
res.add(sb.toString());
return;
}
for (int i = 0; i < n; i++) {
if (used[i]) continue; // 如果当前字符串已在路径中,跳过
// 树层去重:跳过与前一个相同且前一个未使用的字符串
if (i > 0 && arr[i].equals(arr[i - 1]) && !used[i - 1]) continue;
// 选择当前字符串并标记为已使用
path.addLast(arr[i]);
used[i] = true;
// 递归生成下一个字符串组合
dfs(used, path, res);
// 回溯:取消当前字符串的使用标记,并移除其在路径中的位置
used[i] = false;
path.removeLast();
}
}
}
Python参考代码
n = int(input())
arr = input().split()
# 全局变量
path = [] # 存储当前的字符串组合路径
used = [False] * n # 用于标记哪些字符串已经在当前路径中使用过
cache = set() # 用来存储最终生成的唯一组合
# 全排列求解
def dfs():
if len(path) == n:
# 当路径长度等于 n 时,生成一个完整的组合,将其加入 cache 集合中
cache.add("".join(path))
return
for i in range(n):
if used[i]:
continue # 如果当前字符串已在路径中,跳过
# 树层去重:跳过与前一个相同且前一个未使用的字符串
if i > 0 and arr[i] == arr[i-1] and not used[i-1]:
continue
# 选择当前字符串并标记为已使用
path.append(arr[i])
used[i] = True
# 递归生成下一个字符串组合
dfs()
# 回溯:取消当前字符串的使用标记,并移除其在路径中的位置
used[i] = False
path.pop()
# 排序是为了让相同元素相邻,方便后面树层去重
arr.sort()
# 开始深度优先搜索
dfs()
# 输出石碑文字的组合(按照升序排列)
for v in sorted(list(cache)):
print(v)