
华为OD机试 2024E卷题库疯狂收录中,刷题点这里
专栏导读
本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。
一、题目描述
有一种特殊的加密算法,明文为一段数字串,经过密码本查找转换,生成另一段密文数字串。
规则如下
- 明文为一段数字串由0-9组成
 - 密码本为数字0-9组成的二维数组
 - 需要按明文串的数字顺序在密码本里找到同样的数字串,密码本里的数字串是由相邻的单元格数字组成,上下和左右是相邻的,注意:对角线不相邻,同一个单元格的数字不能重复使用。
 - 每一位明文对应密文即为密码本中找到的单元格所在的行和列序号(序号从0开始)组成的两个数字。如明文第位Data[i]对应密码本单元格为Book[x][y],则明文第i位对应的密文为XY,X和Y之间用空格隔开
 
如果有多条密文,返回字符序最小的密文。如果密码本无法匹配,返回"error"
二、输入描述
第一行输入1个正整数N,代表明文的长度(1<= N <= 200)
第二行输入N个明文数字组成的序列Data[i] (整数: 0<= Data[i]<= 9)
第三行1个正整数M,代表密文的长度接下来M行,每行M个数,代表密文矩阵
三、输出描述
输出字典序最小密文.如果无法匹配,输出"error
四、测试用例
测试用例1:
1、输入
2
 0 3
 3
 0 0 2
 1 3 4
 6 6 4
2、输出
0 1 1 1
3、说明
目标是找到密码本中所有与明文匹配的路径,并输出字典序最小的路径。
- 从位置 (0, 0) 开始: 
  
- 当前单元格值是 0,匹配明文的第一个数字 0。
 - 接下来在相邻单元格中寻找 3,可选的相邻单元格有:
 - (0, 1) -> 值是 0
 - (1, 0) -> 值是 1
 - 没有找到匹配的数字 3,继续搜索下一个起点。
 
 - 从位置 (0, 1) 开始: 
  
- 当前单元格值是 0,匹配明文的第一个数字 0。
 - 接下来在相邻单元格中寻找 3,可选的相邻单元格有:
 - (0, 0) -> 值是 0
 - (0, 2) -> 值是 2
 - (1, 1) -> 值是 3(匹配!)
 - 路径为:(0, 1) -> (1, 1),对应的密文为 0 1 1 1。
 
 - 从其他起点继续搜索(如(0, 2), (1, 0), 等),但这些都不能找到有效的路径。
 
在所有可能的路径中,唯一找到的路径是从 (0, 1) -> (1, 1),对应的密文为 0 1 1 1。
测试用例2:
1、输入
2
 0 5
 3
 0 0 2
 1 3 4
 6 6 4
2、输出
error
3、说明
- 从位置 (0, 0) 开始: 
  
- 当前单元格值是 0,匹配明文的第一个数字 0。
 - 接下来在相邻单元格中寻找 5,可选的相邻单元格有:
 - (0, 1) -> 值是 0
 - (1, 0) -> 值是 1
 - 没有找到匹配的数字 5,继续搜索下一个起点。
 
 - 从位置 (0, 1) 开始: 
  
- 当前单元格值是 0,匹配明文的第一个数字 0。
 - 接下来在相邻单元格中寻找 5,可选的相邻单元格有:
 - (0, 0) -> 值是 0
 - (0, 2) -> 值是 2
 - (1, 1) -> 值是 3
 - 没有找到匹配的数字 5,继续搜索下一个起点。
 
 - 从其他起点继续搜索(如(0, 2), (1, 0), 等),但这些都不能找到有效的路径。
 
在密码本中,数字 5 并不存在,因此没有找到任何匹配路径。
五、解题思路
1、深度优先搜索DFS
DFS (Depth-First Search) 是一种用于遍历或搜索树或图的算法。它从一个起点出发,尽可能深入探索每一个分支,直到不能再深入为止,然后回溯到上一个节点继续探索其他分支。
在本题中,DFS 用于在密码本矩阵中查找与明文数字序列匹配的路径。每找到一个符合条件的单元格,继续递归搜索其相邻单元格,直到找到完整的明文序列或搜索失败。
2、为什么采用深度优先搜索DFS?
采用深度优先搜索(DFS)算法有以下几个原因:
DFS 是一种遍历或搜索图或树数据结构的算法,特别适用于路径搜索问题。因为它会从一个起点出发,尽可能深入到某个分支的尽头,然后回溯,探索其他未访问的分支。在本题中,我们需要在二维矩阵中找到一条特定的路径,DFS 是一种合适的选择。
在本题中,密码本的大小是有限的(最大 200x200),这使得 DFS 在时间和空间复杂度上是可接受的。DFS 的时间复杂度是 O(V + E),其中 V 是节点数,E 是边数。对于矩阵搜索问题,节点数和边数都是有限的,因此 DFS 是有效的。
3、具体步骤:
- 读取输入:从标准输入中读取明文长度、明文数字序列、密码本矩阵的大小和内容。
 - 初始化数据结构:准备用于深度优先搜索(DFS)的辅助数据结构,包括密码本矩阵、访问记录矩阵和用于构建路径的字符串生成器。
 - 遍历起点:从密码本矩阵中的每一个可能的起点开始,尝试匹配明文数字序列。
 - 深度优先搜索(DFS):递归搜索从当前起点出发的所有可能路径: 
  
- 检查当前单元格是否与明文的当前数字匹配。
 - 如果匹配,继续搜索其相邻单元格(上下左右四个方向)。
 - 在每一步搜索中,记录路径并标记已访问单元格,避免重复使用。
 - 如果找到一条完整的路径,与当前已找到的最优路径比较,更新最优路径。
 
 - 输出结果:遍历完所有可能的起点后,输出字典序最小的路径,如果没有找到匹配路径,输出 “error”。
 
六、Python算法源码
def dfs(book, data, visited, x, y, index, M, path, N):
    global minPath
    # 如果已经匹配到明文的最后一个数字
    if index == N - 1:
        current_path = " ".join(map(str, path))
        # 更新字典序最小的路径
        if minPath is None or current_path < minPath:
            minPath = current_path
        return
    # 定义方向数组,用于表示上下左右四个方向
    dx = [-1, 1, 0, 0]
    dy = [0, 0, -1, 1]
    # 遍历四个方向
    for i in range(4):
        new_x = x + dx[i]
        new_y = y + dy[i]
        # 检查新的位置是否在矩阵范围内,是否未访问过,且与明文的下一个数字匹配
        if 0 <= new_x < M and 0 <= new_y < M and not visited[new_x][new_y] and book[new_x][new_y] == data[index + 1]:
            visited[new_x][new_y] = True  # 标记新的位置为访问过
            path.append((new_x, new_y))   # 更新路径
            # 继续深度优先搜索
            dfs(book, data, visited, new_x, new_y, index + 1, M, path, N)
            path.pop()                    # 回溯,移除最后添加的路径
            visited[new_x][new_y] = False  # 取消访问标记
def main():
    global minPath
    minPath = None
    # 读取输入
    N = int(input())
    data = list(map(int, input().split()))
    M = int(input())
    book = [list(map(int, input().split())) for _ in range(M)]
    # 尝试从每一个位置开始搜索
    for i in range(M):
        for j in range(M):
            # 如果当前单元格的值与明文的第一个数字匹配
            if book[i][j] == data[0]:
                visited = [[False] * M for _ in range(M)]  # 记录访问过的单元格
                visited[i][j] = True  # 标记当前单元格为访问过
                # 从当前位置开始深度优先搜索
                dfs(book, data, visited, i, j, 0, M, [(i, j)], N)
    # 输出结果
    print(minPath if minPath is not None else "error")
if __name__ == "__main__":
    main()
 
七、JavaScript算法源码
let dx = [-1, 1, 0, 0];
let dy = [0, 0, -1, 1];
let minPath = null; // 存储字典序最小的路径
function dfs(book, data, visited, x, y, index, M, path, N) {
    // 如果已经匹配到明文的最后一个数字
    if (index === N - 1) {
        let currentPath = path.join(" ");
        // 更新字典序最小的路径
        if (minPath === null || currentPath < minPath) {
            minPath = currentPath;
        }
        return;
    }
    // 遍历四个方向
    for (let i = 0; i < 4; i++) {
        let newX = x + dx[i];
        let newY = y + dy[i];
        // 检查新的位置是否在矩阵范围内,是否未访问过,且与明文的下一个数字匹配
        if (newX >= 0 && newX < M && newY >= 0 && newY < M && !visited[newX][newY] && book[newX][newY] === data[index + 1]) {
            visited[newX][newY] = true;  // 标记新的位置为访问过
            path.push(newX + " " + newY);  // 更新路径
            // 继续深度优先搜索
            dfs(book, data, visited, newX, newY, index + 1, M, path, N);
            path.pop();  // 回溯,移除最后添加的路径
            visited[newX][newY] = false;  // 取消访问标记
        }
    }
}
function main() {
    const input = prompt("请输入数据:").split("\n");
    let inputIndex = 0;
    // 读取明文长度
    const N = parseInt(input[inputIndex++]);
    // 读取明文数字序列
    const data = input[inputIndex++].split(" ").map(Number);
    // 读取密码本的大小
    const M = parseInt(input[inputIndex++]);
    // 读取密码本矩阵
    let book = [];
    for (let i = 0; i < M; i++) {
        book.push(input[inputIndex++].split(" ").map(Number));
    }
    // 尝试从每一个位置开始搜索
    for (let i = 0; i < M; i++) {
        for (let j = 0; j < M; j++) {
            // 如果当前单元格的值与明文的第一个数字匹配
            if (book[i][j] === data[0]) {
                let visited = Array.from({ length: M }, () => Array(M).fill(false));  // 记录访问过的单元格
                visited[i][j] = true;  // 标记当前单元格为访问过
                // 从当前位置开始深度优先搜索
                dfs(book, data, visited, i, j, 0, M, [`${i} ${j}`], N);
            }
        }
    }
    // 输出结果
    console.log(minPath === null ? "error" : minPath);
}
// 调用主函数
main();
 
八、C算法源码
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define MAX_M 100
#define MAX_PATH_LEN 1000
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
char minPath[MAX_PATH_LEN];  // 存储字典序最小的路径
void dfs(int book[MAX_M][MAX_M], int data[], bool visited[MAX_M][MAX_M], int x, int y, int index, int M, char path[], int N) {
    // 如果已经匹配到明文的最后一个数字
    if (index == N - 1) {
        if (strlen(minPath) == 0 || strcmp(path, minPath) < 0) {
            strcpy(minPath, path);
        }
        return;
    }
    // 遍历四个方向
    for (int i = 0; i < 4; i++) {
        int newX = x + dx[i];
        int newY = y + dy[i];
        // 检查新的位置是否在矩阵范围内,是否未访问过,且与明文的下一个数字匹配
        if (newX >= 0 && newX < M && newY >= 0 && newY < M && !visited[newX][newY] && book[newX][newY] == data[index + 1]) {
            visited[newX][newY] = true;  // 标记新的位置为访问过
            char newPath[MAX_PATH_LEN];
            sprintf(newPath, "%s %d %d", path, newX, newY);  // 更新路径
            // 继续深度优先搜索
            dfs(book, data, visited, newX, newY, index + 1, M, newPath, N);
            visited[newX][newY] = false;  // 取消访问标记
        }
    }
}
int main() {
    int N, M;
    int data[MAX_M];
    int book[MAX_M][MAX_M];
    bool visited[MAX_M][MAX_M] = { false };  // 访问记录矩阵
    // 初始化 minPath
    memset(minPath, 0, sizeof(minPath));
    // 读取明文长度
    scanf("%d", &N);
    // 读取明文数字序列
    for (int i = 0; i < N; i++) {
        scanf("%d", &data[i]);
    }
    // 读取密码本的大小
    scanf("%d", &M);
    // 读取密码本矩阵
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            scanf("%d", &book[i][j]);
        }
    }
    // 尝试从每一个位置开始搜索
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            // 如果当前单元格的值与明文的第一个数字匹配
            if (book[i][j] == data[0]) {
                visited[i][j] = true;  // 标记当前单元格为访问过
                char startPath[MAX_PATH_LEN];
                sprintf(startPath, "%d %d", i, j);
                // 从当前位置开始深度优先搜索
                dfs(book, data, visited, i, j, 0, M, startPath, N);
                visited[i][j] = false;  // 取消访问标记
            }
        }
    }
    // 输出结果
    if (strlen(minPath) == 0) {
        printf("error\n");
    } else {
        printf("%s\n", minPath);
    }
    return 0;
}
 
九、C++算法源码
#include <iostream>
#include <vector>
#include <string>
#include <climits>
using namespace std;
vector<int> dx = {-1, 1, 0, 0};
vector<int> dy = {0, 0, -1, 1};
string minPath;  // 存储字典序最小的路径
void dfs(const vector<vector<int>>& book, const vector<int>& data, vector<vector<bool>>& visited, int x, int y, int index, int M, string path, int N) {
    // 如果已经匹配到明文的最后一个数字
    if (index == N - 1) {
        if (minPath.empty() || path < minPath) {
            minPath = path;
        }
        return;
    }
    // 遍历四个方向
    for (int i = 0; i < 4; i++) {
        int newX = x + dx[i];
        int newY = y + dy[i];
        // 检查新的位置是否在矩阵范围内,是否未访问过,且与明文的下一个数字匹配
        if (newX >= 0 && newX < M && newY >= 0 && newY < M && !visited[newX][newY] && book[newX][newY] == data[index + 1]) {
            visited[newX][newY] = true;  // 标记新的位置为访问过
            dfs(book, data, visited, newX, newY, index + 1, M, path + " " + to_string(newX) + " " + to_string(newY), N);
            visited[newX][newY] = false;  // 取消访问标记
        }
    }
}
int main() {
    int N, M;
    cin >> N;
    vector<int> data(N);
    for (int i = 0; i < N; i++) {
        cin >> data[i];
    }
    cin >> M;
    vector<vector<int>> book(M, vector<int>(M));
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            cin >> book[i][j];
        }
    }
    minPath = "";
    // 尝试从每一个位置开始搜索
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            // 如果当前单元格的值与明文的第一个数字匹配
            if (book[i][j] == data[0]) {
                vector<vector<bool>> visited(M, vector<bool>(M, false));  // 记录访问过的单元格
                visited[i][j] = true;  // 标记当前单元格为访问过
                dfs(book, data, visited, i, j, 0, M, to_string(i) + " " + to_string(j), N);
            }
        }
    }
    // 输出结果
    if (minPath.empty()) {
        cout << "error" << endl;
    } else {
        cout << minPath << endl;
    }
    return 0;
}
 
🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 2024 E卷 200分)
🏆本文收录于,华为OD机试真题(Python/JS/C/C++)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。




















