华为OD 2024笔试机试 - 考古学家考古问题 (Java/c++/python C卷D卷真题算法)

华为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)

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值