专栏导读
本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。
一、题目描述
有一种特殊的加密算法,明文为一段数字串,经过密码本查找转换,生成另一段密文数字串。规则如下
- 明文为一段数字串由0-9组成
- 密码本为数字0-9组成的二维数组
- 需要按明文串的数字顺序在密码本里找到同样的数字串,密码本里的数字串是由相邻的单元格数字组成,上下和左右是相邻的,注意:对角线不相邻,同一个单元格的数字不能重复使用。
- 每一位明文对应密文即为密码本中找到的单元格所在的行和列序号(序号从0开始)组成的两个数字。如明文第i位Data[i]对应密码本单元格为Book[X][Y],则明文第i位对应的密文为X Y,X和Y之间用空格隔开。如果有多条密文,返回字符序最小的密文。如果密码本无法匹配,返回"error".
请你设计这个加密程序。
示例 1:
密码本:
{0,0,2},
{1,3,4},
{6,6,4}
明文"3",密文"1 1"
示例 2:
密码本:
{0,0,2},
{1,3,4},
{6,6,4}
明文"0 3",密文"0 1 1 1"
二、输入描述
第一行输入1个正整数N,代表明文的长度(1 <= N <= 9)
第二行输入N个明文数字组成的序列Data[i](整数,0 <= Data[i] <= 9)
第三行输入1个正整数M,(1 <= M <= 9)
接下来输入一个M*M的矩阵代表密码本Book[i][i],(整数,0 <= Book[i][i] <= 9)
三、输出描述
如明文 第i位Data[i]对应密码本单元格为Book[i][j],则明文第i位对应的密文为X Y,X和Y之间用空格隔开。如果有多条密文,返回字符序最小的密文。如果密码本无法匹配,返回"error"。
四、测试用例
测试用例1
1、输入
1
3
3
0 0 2
1 3 4
6 6 4
2、输出
1 1
3、说明
明文只有一个数字"3",在密码本中位置为(1,1),所以密文为"1 1"。
测试用例2
1、输入
2
5 7
3
0 0 2
1 3 4
6 6 5
2、输出
error
3、说明
明文为"5 7",在密码本中可以找到"5",但没有与"5"相邻的"7",所以无法找到匹配路径,返回"error"。
五、解题思路
1、解题思路
首先在密码本中找到所有与明文第一个数字匹配的单元格作为起点。
从每个起点开始,使用DFS搜索与明文匹配的完整路径。
对于每个有效路径,构建对应的密文(单元格坐标序列)。
如果找到多个可能的密文,按字典序排序并返回最小的一个。
如果没有找到匹配的路径,返回"error"。
2、数据结构和算法选择
二维数组(密码本):用于存储密码本的数据。
深度优先搜索(DFS):用于在密码本中寻找匹配明文的路径。
回溯算法:在DFS中使用回溯来尝试所有可能的路径。
3、为什么采用这些数据结构和算法
二维数组:密码本本身就是一个MxM的矩阵,使用二维数组最为直观和高效。
深度优先搜索(DFS):
- 适合在网格中寻找路径的问题。
- 能够探索所有可能的方向(上、右、下、左)。
- 结合回溯可以穷举所有可能的路径。
回溯算法:当发现当前路径不满足条件时,可以回退到上一步,尝试其他可能的路径。对于搜索所有可能解的问题非常有效。
六、Python算法源码
def main():
# 读取明文长度 N
N = int(input())
# 读取明文数字序列
plaintext = list(map(int, input().split()))
# 读取密码本大小 M
M = int(input())
# 读取密码本
codeBook = []
for _ in range(M):
codeBook.append(list(map(int, input().split())))
# 四个方向:上、右、下、左
dr = [-1, 0, 1, 0] # 行变化
dc = [0, 1, 0, -1] # 列变化
# 找到所有可能的密文
def findAllCiphertexts():
allCiphertexts = []
# 遍历密码本中的每一个单元格作为起始点
for i in range(M):
for j in range(M):
# 如果起始单元格匹配明文的第一个数字
if codeBook[i][j] == plaintext[0]:
# 记录已访问的单元格
visited = [[False for _ in range(M)] for _ in range(M)]
visited[i][j] = True
# 记录当前路径
path = [(i, j)]
# 开始深度优先搜索
dfs(1, i, j, visited, path, allCiphertexts)
return allCiphertexts
# 使用DFS搜索匹配明文的路径
def dfs(index, r, c, visited, path, allCiphertexts):
# 如果已经匹配了整个明文
if index == len(plaintext):
# 构建密文字符串
ciphertext = ""
for i in range(len(path)):
coord = path[i]
ciphertext += str(coord[0]) + " " + str(coord[1])
if i < len(path) - 1:
ciphertext += " "
# 添加到密文列表
allCiphertexts.append(ciphertext)
return
# 尝试四个方向的相邻单元格
for d in range(4):
nr = r + dr[d]
nc = c + dc[d]
# 检查新单元格是否在密码本范围内,且未被访问过,且匹配明文当前字符
if 0 <= nr < M and 0 <= nc < M and not visited[nr][nc] and codeBook[nr][nc] == plaintext[index]:
# 标记为已访问
visited[nr][nc] = True
# 添加到当前路径
path.append((nr, nc))
# 继续搜索下一个字符
dfs(index + 1, nr, nc, visited, path, allCiphertexts)
# 回溯
path.pop()
visited[nr][nc] = False
allCiphertexts = findAllCiphertexts()
if not allCiphertexts:
# 如果没有找到匹配的密文,返回 "error"
print("error")
else:
# 按字典序排序,返回最小的密文
allCiphertexts.sort()
print(allCiphertexts[0])
if __name__ == "__main__":
main()
七、JavaScript算法源码
// 使用Node.js运行JavaScript代码,通过process.stdin和process.stdout进行输入输出
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const lines = [];
let lineIndex = 0;
rl.on('line', (line) => {
lines.push(line);
});
rl.on('close', () => {
// 读取明文长度 N
const N = parseInt(lines[lineIndex++]);
// 读取明文数字序列
const plaintext = lines[lineIndex++].split(' ').map(Number);
// 读取密码本大小 M
const M = parseInt(lines[lineIndex++]);
// 读取密码本
const codeBook = [];
for (let i = 0; i < M; i++) {
codeBook.push(lines[lineIndex++].split(' ').map(Number));
}
// 四个方向:上、右、下、左
const dr = [-1, 0, 1, 0]; // 行变化
const dc = [0, 1, 0, -1]; // 列变化
// 找到所有可能的密文
function findAllCiphertexts() {
const allCiphertexts = [];
// 遍历密码本中的每一个单元格作为起始点
for (let i = 0; i < M; i++) {
for (let j = 0; j < M; j++) {
// 如果起始单元格匹配明文的第一个数字
if (codeBook[i][j] === plaintext[0]) {
// 记录已访问的单元格
const visited = Array(M).fill().map(() => Array(M).fill(false));
visited[i][j] = true;
// 记录当前路径
const path = [[i, j]];
// 开始深度优先搜索
dfs(1, i, j, visited, path, allCiphertexts);
}
}
}
return allCiphertexts;
}
// 使用DFS搜索匹配明文的路径
function dfs(index, r, c, visited, path, allCiphertexts) {
// 如果已经匹配了整个明文
if (index === plaintext.length) {
// 构建密文字符串
let ciphertext = "";
for (let i = 0; i < path.length; i++) {
const coord = path[i];
ciphertext += coord[0] + " " + coord[1];
if (i < path.length - 1) {
ciphertext += " ";
}
}
// 添加到密文列表
allCiphertexts.push(ciphertext);
return;
}
// 尝试四个方向的相邻单元格
for (let d = 0; d < 4; d++) {
const nr = r + dr[d];
const nc = c + dc[d];
// 检查新单元格是否在密码本范围内,且未被访问过,且匹配明文当前字符
if (nr >= 0 && nr < M && nc >= 0 && nc < M && !visited[nr][nc] && codeBook[nr][nc] === plaintext[index]) {
// 标记为已访问
visited[nr][nc] = true;
// 添加到当前路径
path.push([nr, nc]);
// 继续搜索下一个字符
dfs(index + 1, nr, nc, visited, path, allCiphertexts);
// 回溯
path.pop();
visited[nr][nc] = false;
}
}
}
const allCiphertexts = findAllCiphertexts();
if (allCiphertexts.length === 0) {
// 如果没有找到匹配的密文,返回 "error"
console.log("error");
} else {
// 按字典序排序,返回最小的密文
allCiphertexts.sort();
console.log(allCiphertexts[0]);
}
});
八、C算法源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// 四个方向:上、右、下、左
int dr[4] = {-1, 0, 1, 0}; // 行变化
int dc[4] = {0, 1, 0, -1}; // 列变化
// 密文信息结构体
typedef struct {
char* text;
} Ciphertext;
// 路径信息结构体
typedef struct {
int* row;
int* col;
int size;
int capacity;
} Path;
// 初始化路径
Path* createPath(int capacity) {
Path* path = (Path*)malloc(sizeof(Path));
path->row = (int*)malloc(sizeof(int) * capacity);
path->col = (int*)malloc(sizeof(int) * capacity);
path->size = 0;
path->capacity = capacity;
return path;
}
// 释放路径内存
void freePath(Path* path) {
free(path->row);
free(path->col);
free(path);
}
// 添加坐标到路径
void addToPath(Path* path, int r, int c) {
if (path->size >= path->capacity) {
path->capacity *= 2;
path->row = (int*)realloc(path->row, sizeof(int) * path->capacity);
path->col = (int*)realloc(path->col, sizeof(int) * path->capacity);
}
path->row[path->size] = r;
path->col[path->size] = c;
path->size++;
}
// 从路径中移除最后一个坐标
void removeLastFromPath(Path* path) {
if (path->size > 0) {
path->size--;
}
}
// 构建密文字符串
char* buildCiphertext(Path* path) {
char* ciphertext = (char*)malloc(path->size * 10); // 足够大的空间
ciphertext[0] = '\0';
for (int i = 0; i < path->size; i++) {
char buffer[10];
sprintf(buffer, "%d %d", path->row[i], path->col[i]);
strcat(ciphertext, buffer);
if (i < path->size - 1) {
strcat(ciphertext, " ");
}
}
return ciphertext;
}
// 比较两个密文字符串
int compareCiphertexts(const void* a, const void* b) {
return strcmp((*(Ciphertext*)a).text, (*(Ciphertext*)b).text);
}
// DFS搜索匹配明文的路径
void dfs(int* plaintext, int plaintext_len, int** codeBook, int M, int index, int r, int c, bool** visited, Path* path, Ciphertext** allCiphertexts, int* ciphertext_count, int* capacity) {
// 如果已经匹配了整个明文
if (index == plaintext_len) {
// 构建密文字符串
char* ciphertext = buildCiphertext(path);
// 添加到密文列表
if (*ciphertext_count >= *capacity) {
*capacity *= 2;
*allCiphertexts = (Ciphertext*)realloc(*allCiphertexts, sizeof(Ciphertext) * (*capacity));
}
(*allCiphertexts)[*ciphertext_count].text = ciphertext;
(*ciphertext_count)++;
return;
}
// 尝试四个方向的相邻单元格
for (int d = 0; d < 4; d++) {
int nr = r + dr[d];
int nc = c + dc[d];
// 检查新单元格是否在密码本范围内,且未被访问过,且匹配明文当前字符
if (nr >= 0 && nr < M && nc >= 0 && nc < M && !visited[nr][nc] && codeBook[nr][nc] == plaintext[index]) {
// 标记为已访问
visited[nr][nc] = true;
// 添加到当前路径
addToPath(path, nr, nc);
// 继续搜索下一个字符
dfs(plaintext, plaintext_len, codeBook, M, index + 1, nr, nc, visited, path, allCiphertexts, ciphertext_count, capacity);
// 回溯
removeLastFromPath(path);
visited[nr][nc] = false;
}
}
}
// 找到所有可能的密文
Ciphertext* findAllCiphertexts(int* plaintext, int plaintext_len, int** codeBook, int M, int* ciphertext_count) {
int capacity = 100;
Ciphertext* allCiphertexts = (Ciphertext*)malloc(sizeof(Ciphertext) * capacity);
*ciphertext_count = 0;
// 遍历密码本中的每一个单元格作为起始点
for (int i = 0; i < M; i++) {
for (int j = 0; j < M; j++) {
// 如果起始单元格匹配明文的第一个数字
if (codeBook[i][j] == plaintext[0]) {
// 记录已访问的单元格
bool** visited = (bool**)malloc(sizeof(bool*) * M);
for (int k = 0; k < M; k++) {
visited[k] = (bool*)calloc(M, sizeof(bool));
}
visited[i][j] = true;
// 记录当前路径
Path* path = createPath(plaintext_len);
addToPath(path, i, j);
// 开始深度优先搜索
dfs(plaintext, plaintext_len, codeBook, M, 1, i, j, visited, path, &allCiphertexts, ciphertext_count, &capacity);
// 释放内存
freePath(path);
for (int k = 0; k < M; k++) {
free(visited[k]);
}
free(visited);
}
}
}
return allCiphertexts;
}
int main() {
// 读取明文长度 N
int N;
scanf("%d", &N);
// 读取明文数字序列
int* plaintext = (int*)malloc(sizeof(int) * N);
for (int i = 0; i < N; i++) {
scanf("%d", &plaintext[i]);
}
// 读取密码本大小 M
int M;
scanf("%d", &M);
// 读取密码本
int** codeBook = (int**)malloc(sizeof(int*) * M);
for (int i = 0; i < M; i++) {
codeBook[i] = (int*)malloc(sizeof(int) * M);
for (int j = 0; j < M; j++) {
scanf("%d", &codeBook[i][j]);
}
}
// 找到所有可能的密文
int ciphertext_count;
Ciphertext* allCiphertexts = findAllCiphertexts(plaintext, N, codeBook, M, &ciphertext_count);
if (ciphertext_count == 0) {
// 如果没有找到匹配的密文,返回 "error"
printf("error\n");
} else {
// 按字典序排序,返回最小的密文
qsort(allCiphertexts, ciphertext_count, sizeof(Ciphertext), compareCiphertexts);
printf("%s\n", allCiphertexts[0].text);
}
// 释放内存
for (int i = 0; i < ciphertext_count; i++) {
free(allCiphertexts[i].text);
}
free(allCiphertexts);
for (int i = 0; i < M; i++) {
free(codeBook[i]);
}
free(codeBook);
free(plaintext);
return 0;
}
九、C++算法源码
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
// 四个方向:上、右、下、左
const int dr[4] = {-1, 0, 1, 0}; // 行变化
const int dc[4] = {0, 1, 0, -1}; // 列变化
// 使用DFS搜索匹配明文的路径
void dfs(const vector<int>& plaintext, const vector<vector<int>>& codeBook, int M, int index, int r, int c,
vector<vector<bool>>& visited, vector<pair<int, int>>& path, vector<string>& allCiphertexts) {
// 如果已经匹配了整个明文
if (index == plaintext.size()) {
// 构建密文字符串
string ciphertext = "";
for (int i = 0; i < path.size(); i++) {
ciphertext += to_string(path[i].first) + " " + to_string(path[i].second);
if (i < path.size() - 1) {
ciphertext += " ";
}
}
// 添加到密文列表
allCiphertexts.push_back(ciphertext);
return;
}
// 尝试四个方向的相邻单元格
for (int d = 0; d < 4; d++) {
int nr = r + dr[d];
int nc = c + dc[d];
// 检查新单元格是否在密码本范围内,且未被访问过,且匹配明文当前字符
if (nr >= 0 && nr < M && nc >= 0 && nc < M && !visited[nr][nc] && codeBook[nr][nc] == plaintext[index]) {
// 标记为已访问
visited[nr][nc] = true;
// 添加到当前路径
path.push_back({nr, nc});
// 继续搜索下一个字符
dfs(plaintext, codeBook, M, index + 1, nr, nc, visited, path, allCiphertexts);
// 回溯
path.pop_back();
visited[nr][nc] = false;
}
}
}
// 找到所有可能的密文
vector<string> findAllCiphertexts(const vector<int>& plaintext, const vector<vector<int>>& codeBook, int M) {
vector<string> allCiphertexts;
// 遍历密码本中的每一个单元格作为起始点
for (int i = 0; i < M; i++) {
for (int j = 0; j < M; j++) {
// 如果起始单元格匹配明文的第一个数字
if (codeBook[i][j] == plaintext[0]) {
// 记录已访问的单元格
vector<vector<bool>> visited(M, vector<bool>(M, false));
visited[i][j] = true;
// 记录当前路径
vector<pair<int, int>> path;
path.push_back({i, j});
// 开始深度优先搜索
dfs(plaintext, codeBook, M, 1, i, j, visited, path, allCiphertexts);
}
}
}
return allCiphertexts;
}
int main() {
// 读取明文长度 N
int N;
cin >> N;
// 读取明文数字序列
vector<int> plaintext(N);
for (int i = 0; i < N; i++) {
cin >> plaintext[i];
}
// 读取密码本大小 M
int M;
cin >> M;
// 读取密码本
vector<vector<int>> codeBook(M, vector<int>(M));
for (int i = 0; i < M; i++) {
for (int j = 0; j < M; j++) {
cin >> codeBook[i][j];
}
}
// 找到所有可能的密文
vector<string> allCiphertexts = findAllCiphertexts(plaintext, codeBook, M);
if (allCiphertexts.empty()) {
// 如果没有找到匹配的密文,返回 "error"
cout << "error" << endl;
} else {
// 按字典序排序,返回最小的密文
sort(allCiphertexts.begin(), allCiphertexts.end());
cout << allCiphertexts[0] << endl;
}
return 0;
}
🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 2025 B卷 200分)
🏆本文收录于,华为OD机试真题(Python/JS/C/C++)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。