
专栏导读
本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。
一、题目描述
玩家收集了 N 个魔法咒语碎片,它们可以组合成强大的魔法阵。但并不是所有碎片都能使用——只含咒语中没有重复字母的碎片才是纯净的,可以激活。
请找出所有纯净碎片,输出由所有纯净碎片组成的所有魔法阵方案。
备注: 所有魔法咒语碎片互不重复## 二、输入描述
二、输入描述
魔法咒语碎片为一个字符串,仅由小写字母构成,没有分隔符,字符串长度不超过 26。
魔法咒语碎片数量 N 小于等于 6 个。
三、输出描述
魔法阵方案,由所有纯净碎片组成的长字符串,每个纯净碎片的字符串之间以空格隔开(" ")。
所有魔法阵方案按字母序排列输出。
四、测试用例
测试用例1:
1、输入
fire water earth
2、输出
earth fire water
earth water fire
fire earth water
fire water earth
water earth fire
water fire earth
3、说明
fire、water、earth 内部都没有重复字母,因此三个碎片全部保留。
3 个字符串共有:
3! = 6
种排列。
测试用例2:
1、输入
noon sun sky
2、输出
sky sun
sun sky
3、说明
noon 中字母 n 和 o 都重复出现,因此被过滤。
五、解题思路
1、核心分成两步:
- 筛选纯净碎片
- 每个字符串只包含 a~z,长度不超过 26。
- 使用一个 int 的低 26 位表示 26 个字母是否出现。
- 如果某个字母对应的二进制位已经是 1,说明该字母重复,该字符串直接淘汰。
- 例如 noon 中第二次遇到 o 时即可判断不纯净。
2、生成所有字典序排列
- 先把所有纯净碎片按字典序排序。
- 使用 DFS + 回溯生成全排列。
- 每层都按照排序后的顺序选择未使用字符串,因此生成结果天然就是字典序,不需要最后再次排序。
六、Python算法源码
import sys
def is_pure(s):
"""
判断字符串内部是否存在重复字母。
"""
seen = set()
for ch in s:
# 当前字母已经出现过,说明该碎片不是纯净碎片
if ch in seen:
return False
seen.add(ch)
return True
def main():
# 读取所有以空白字符分隔的字符串
fragments = sys.stdin.read().split()
# 过滤纯净碎片,并提前按字典序排序
pure = sorted([s for s in fragments if is_pure(s)])
used = [False] * len(pure)
path = []
ans = []
def dfs():
"""
使用 DFS + 回溯生成所有全排列。
"""
# 已经使用全部纯净碎片,得到一个完整方案
if len(path) == len(pure):
if path:
ans.append(" ".join(path))
return
/*
Python 中不能使用这种注释形式
*/
# 实际 DFS 写在下面
七、JavaScript算法源码
const fs = require('fs');
const input = fs.readFileSync(0, 'utf8').trim();
// 输入为空时特殊处理,否则按照任意空白字符切分
const fragments = input.length === 0
? []
: input.split(/\s+/);
/**
* 判断字符串内部是否存在重复字母
*/
function isPure(s) {
const seen = new Set();
for (const ch of s) {
// 已经出现过当前字母,则该碎片不是纯净碎片
if (seen.has(ch)) {
return false;
}
seen.add(ch);
}
return true;
}
// 过滤纯净碎片,然后按照字典序排序
const pure = fragments.filter(isPure).sort();
const used = new Array(pure.length).fill(false);
const path = [];
const ans = [];
/**
* DFS + 回溯生成全部排列
*/
function dfs() {
// 已使用所有纯净碎片,得到完整方案
if (path.length === pure.length) {
if (path.length > 0) {
ans.push(path.join(' '));
}
return;
}
/*
* pure 已经按照字典序排序。
*
* 每一层都按照下标从小到大选择尚未使用的碎片,
* 因此最终 DFS 生成的所有方案天然符合字典序。
*/
for (let i = 0; i < pure.length; i++) {
if (used[i]) {
continue;
}
// 做选择
used[i] = true;
path.push(pure[i]);
dfs();
// 回溯,恢复递归前的状态
path.pop();
used[i] = false;
}
}
dfs();
process.stdout.write(ans.join('\n'));
八、C算法源码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_N 6
#define MAX_LEN 26
// 保存所有纯净碎片
char pure[MAX_N][MAX_LEN + 1];
int pureCount = 0;
// used[i] 表示第 i 个纯净碎片是否已经使用
int used[MAX_N];
// path 保存当前排列中每个位置对应的碎片下标
int path[MAX_N];
/**
* 判断一个字符串是否为纯净碎片
*/
int isPure(const char *s)
{
int seen[26] = {0};
for (int i = 0; s[i] != '\0'; i++)
{
int index = s[i] - 'a';
// 当前字母已经出现过,说明存在重复字母
if (seen[index])
{
return 0;
}
seen[index] = 1;
}
return 1;
}
/**
* qsort 使用的字符串比较函数
*/
int cmpString(const void *a, const void *b)
{
return strcmp((const char *)a, (const char *)b);
}
/**
* 使用 DFS + 回溯生成所有排列
*/
void dfs(int depth)
{
/*
* 已经选择了全部纯净碎片,
* 当前 path 就是一种完整的魔法阵方案。
*/
if (depth == pureCount)
{
// 没有纯净碎片时不输出空方案
if (pureCount == 0)
{
return;
}
for (int i = 0; i < pureCount; i++)
{
if (i > 0)
{
printf(" ");
}
printf("%s", pure[path[i]]);
}
printf("\n");
return;
}
/*
* pure 已经提前按照字典序排序。
*
* 当前层按照下标从小到大选择碎片,
* 因此 DFS 的最终输出天然按照字典序排列。
*/
for (int i = 0; i < pureCount; i++)
{
if (used[i])
{
continue;
}
// 做选择
used[i] = 1;
path[depth] = i;
dfs(depth + 1);
// 回溯:释放当前碎片
used[i] = 0;
}
}
int main(void)
{
char fragment[MAX_LEN + 1];
/*
* 每次读取一个碎片,直到输入结束。
*
* %26s 可以避免字符串超过数组空间。
*/
while (scanf("%26s", fragment) == 1)
{
if (isPure(fragment))
{
strcpy(pure[pureCount], fragment);
pureCount++;
}
}
// 对所有纯净碎片按照字典序排序
qsort(
pure,
pureCount,
sizeof(pure[0]),
cmpString
);
dfs(0);
return 0;
}
九、C++算法源码
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
/**
* 判断当前碎片内部是否存在重复字母
*/
bool isPure(const string& s)
{
bool seen[26] = {false};
for (char c : s)
{
int index = c - 'a';
// 当前字母已经出现过,说明该碎片不纯净
if (seen[index])
{
return false;
}
seen[index] = true;
}
return true;
}
/**
* DFS + 回溯生成所有纯净碎片的全排列
*/
void dfs(
const vector<string>& pure,
vector<bool>& used,
vector<string>& path,
vector<string>& ans)
{
/*
* 已经选择全部纯净碎片,
* 当前 path 就是一种完整魔法阵方案。
*/
if (path.size() == pure.size())
{
if (!path.empty())
{
string line;
for (size_t i = 0; i < path.size(); ++i)
{
if (i > 0)
{
line += ' ';
}
line += path[i];
}
ans.push_back(line);
}
return;
}
/*
* pure 已经按照字典序排序,
* 按下标从小到大进行 DFS,
* 最终结果天然也是按照字典序排列。
*/
for (size_t i = 0; i < pure.size(); ++i)
{
if (used[i])
{
continue;
}
// 做选择
used[i] = true;
path.push_back(pure[i]);
dfs(pure, used, path, ans);
// 回溯,恢复递归之前的状态
path.pop_back();
used[i] = false;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<string> pure;
string fragment;
// 不断读取碎片直到输入结束
while (cin >> fragment)
{
if (isPure(fragment))
{
pure.push_back(fragment);
}
}
// 提前排序,使 DFS 能直接按照字典序产生结果
sort(pure.begin(), pure.end());
vector<bool> used(pure.size(), false);
vector<string> path;
vector<string> ans;
dfs(pure, used, path, ans);
// 输出所有排列
for (size_t i = 0; i < ans.size(); ++i)
{
if (i > 0)
{
cout << '\n';
}
cout << ans[i];
}
return 0;
}
🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 新系统 200分)
🏆本文收录于,华为OD机试真题(Python/JS/C/C++)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

371

被折叠的 条评论
为什么被折叠?



