返回目录

题目描述

有一种特殊的加密算法,明文为一段数字串,经过密码本查找转换,生成另一段密文数字串。

规则如下:

  1. 明文为一段数字串由 0\~9 组成
  2. 密码本为数字 0\~9 组成的二维数组
  3. 需要按明文串的数字顺序在密码本里找到同样的数字串,密码本里的数字串是由相邻的单元格数字组成,上下和左右是相邻的,注意:对角线不相邻,同一个单元格的数字不能重复使用。
  4. 每一位明文对应密文即为密码本中找到的单元格所在的行和列序号(序号从0开始)组成的两个数宇。
    如明文第 i 位 Data[i] 对应密码本单元格为 Bookx,则明文第 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”

示例3:

密码本:

0 0 2 4

1 3 4 6

3 4 1 5

6 6 6 5

明文:“0 0 2 4”,密文:“0 0 0 1 0 2 0 3” 和 “0 0 0 1 0 2 1 2”,返回字典序最小的"0 0 0 1 0 2 0 3"

明文:“8 2 2 3”,密文:“error”,密码本中无法匹配

输入描述

第一行输入 1 个正整数 N,代表明文的长度(1 ≤ N ≤ 200)

第二行输入 N 个明文组成的序列 Data[i](0 ≤ Data[i] ≤ 9)

第三行输入 1 个正整数 M,代表密文的长度

接下来 M 行,每行 M 个数,代表密文矩阵

输出描述

输出字典序最小密文,如果无法匹配,输出"error"

示例:

输入2
0 3
3
0 0 2
1 3 4
 6 6 4
输出0 1 1 1

解题思路

核心解题思路是通过深度优先搜索(DFS)在一个给定的密码本(二维数组)中找到一条路径,使得这条路径上的数字序列与给定的明文数字序列相匹配,并且在所有可能的匹配路径中选择一条字典序最小的作为密文路径输出。具体步骤如下:

  1. 初始化变量

    • nm 分别存储明文的长度和密码本的尺寸。
    • book 二维数组存储密码本内容。
    • directions 数组表示搜索的四个方向(右、下、左、上)。
    • minPath 字符串用于存储找到的字典序最小的密文路径。
    • found 布尔变量标记是否找到至少一种加密方式。
  2. 搜索准备

    • 创建一个 visited 布尔二维数组来标记密码本中的数字是否已被访问,以避免重复搜索。
  3. 开始搜索

    • 遍历密码本的每个数字,当找到一个数字与明文的第一个数字相匹配时,从该位置开始使用深度优先搜索(DFS)。
  4. 深度优先搜索(DFS)

    • 递归地搜索所有可能的路径。对于当前位置,如果满足以下条件之一,则继续搜索:

      • 当前位置的数字与明文的当前索引指向的数字相匹配。
      • 已到达明文的末尾,且路径符合条件(字典序最小或找到的第一条路径)。
    • 在每个位置,尝试向四个方向移动,并递归调用 dfs 方法继续搜索。
    • 搜索过程中,使用 visited 数组标记当前位置已访问,以避免循环访问。
  5. 回溯与更新

    • 每当找到一条完整的匹配路径时,比较并更新 minPath 为字典序最小的路径。
    • 完成当前路径的搜索后,回溯(撤销当前位置的访问标记),尝试其他可能的路径。
  6. 输出结果

    • 如果找到至少一条匹配的路径(即 foundtrue),则输出字典序最小的密文路径。
    • 否则,输出 "error" 表示无法在密码本中找到与明文完全匹配的路径。

Python算法源码

# 全局变量定义
directions = [(0, 1), (1, 0), (-1, 0), (0, -1)]  # 表示四个搜索方向:右、下、左、上
min_path = ""  # 用于存储找到的字典序最小的密文路径
found = False  # 标记是否找到了至少一种加密方式

# 深度优先搜索函数定义
def dfs(data, index, x, y, visited, path):
    global min_path, found
    n = len(data)
    m = len(visited)

    if index == n:  # 如果已经处理完所有明文数字
        if not found or path < min_path:  # 如果找到的是第一种加密方式,或者字典序比之前的小
            min_path = path  # 更新最小字典序密文路径
        found = True  # 标记找到了加密方式
        return

    if x < 0 or y < 0 or x >= m or y >= m or visited[x][y] or book[x][y] != data[index]:
        # 如果坐标越界,或该位置已访问,或该位置数字与明文不匹配,则返回
        return

    visited[x][y] = True  # 标记当前位置已访问
    new_path = path + f"{x} {y} "  # 更新路径字符串

    # 遍历四个方向
    for dir_x, dir_y in directions:
        new_x = x + dir_x
        new_y = y + dir_y
        dfs(data, index + 1, new_x, new_y, visited, new_path)  # 在新方向上搜索下一个明文数字

    visited[x][y] = False  # 回溯,撤销当前位置的访问标记


if __name__ == "__main__":
    # 读取明文的长度
    n = int(input())
    # 创建列表存储明文数字
    data = list(map(int, input().split()))

    # 读取密码本的尺寸
    m = int(input())
    # 初始化密码本列表
    book = [list(map(int, input().split())) for _ in range(m)]

    # 标记密码本中的数字是否已经被访问过
    visited = [[False] * m for _ in range(m)]

    for i in range(m):
        for j in range(m):
            if book[i][j] == data[0]:  # 从找到的第一个数字开始搜索
                dfs(data, 0, i, j, visited, "")

    print(min_path if found else "error")  # 如果找到至少一种加密方式,输出最小字典序的密文;否则,输出"error"

C算法源码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 定义全局变量
static int n, m; // 分别用于存储明文的长度和密码本的尺寸
int **book; // 用于存储密码本,是一个二维数组
int directions[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; // 表示四个搜索方向:右、下、左、上
char minPath[1000]; // 用于存储找到的字典序最小的密文路径
bool found = false; // 标记是否找到了至少一种加密方式

// 深度优先搜索函数声明
void dfs(const int *data, int index, int x, int y, bool **visited, char *path);

int main() {
    scanf("%d", &n); // 读取明文的长度
    int *data = (int *)malloc(n * sizeof(int)); // 创建数组存储明文数字
    for (int i = 0; i < n; ++i) {
        scanf("%d", &data[i]); // 读取每个明文数字
    }

    scanf("%d", &m); // 读取密码本的尺寸
    book = (int **)malloc(m * sizeof(int *)); // 初始化密码本数组
    for (int i = 0; i < m; ++i) {
        book[i] = (int *)malloc(m * sizeof(int));
        for (int j = 0; j < m; ++j) {
            scanf("%d", &book[i][j]); // 填充密码本内容
        }
    }

    bool **visited = (bool **)malloc(m * sizeof(bool *)); // 标记密码本中的数字是否已经被访问过
    for (int i = 0; i < m; ++i) {
        visited[i] = (bool *)malloc(m * sizeof(bool));
        for (int j = 0; j < m; ++j) {
            visited[i][j] = false;
        }
    }

    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < m; ++j) {
            if (book[i][j] == data[0]) { // 从找到的第一个数字开始搜索
                dfs(data, 0, i, j, visited, ""); // 使用深度优先搜索找到所有可能的加密路径
            }
        }
    }

    printf("%s\n", (found ? minPath : "error")); // 如果找到至少一种加密方式,输出最小字典序的密文;否则,输出"error"

    // 释放动态分配的内存
    for (int i = 0; i < m; ++i) {
        free(book[i]);
        free(visited[i]);
    }
    free(book);
    free(visited);
    free(data);

    return 0;
}

void dfs(const int *data, int index, int x, int y, bool **visited, char *path) {
    if (index == n) { // 如果已经处理完所有明文数字
        if (!found || strcmp(path, minPath) < 0) { // 如果找到的是第一种加密方式,或者字典序比之前的小
            strcpy(minPath, path); // 更新最小字典序密文路径
        }
        found = true; // 标记找到了加密方式
        return;
    }

    if (x < 0 || y < 0 || x >= m || y >= m || visited[x][y] || book[x][y] != data[index]) {
        // 如果坐标越界,或该位置已访问,或该位置数字与明文不匹配,则返回
        return;
    }

    visited[x][y] = true; // 标记当前位置已访问
    char newPath[1000];
    sprintf(newPath, "%s%d %d ", path, x, y); // 更新路径字符串

    // 遍历四个方向
    for (int i = 0; i < 4; ++i) {
        int newX = x + directions[i][0];
        int newY = y + directions[i][1];
        dfs(data, index + 1, newX, newY, visited, newPath); // 在新方向上搜索下一个明文数字
    }

    visited[x][y] = false; // 回溯,撤销当前位置的访问标记
}

Java算法源码

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    // 全局变量定义
    private static int n, m; // 分别用于存储明文的长度和密码本的尺寸
    private static List<List<Integer>> book; // 用于存储密码本,是一个二维向量
    private static List<List<Integer>> directions = new ArrayList<>(); // 表示四个搜索方向:右、下、左、上
    private static String minPath = ""; // 用于存储找到的字典序最小的密文路径
    private static boolean found = false; // 标记是否找到了至少一种加密方式

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        n = scanner.nextInt(); // 读取明文的长度
        List<Integer> data = new ArrayList<>(); // 创建向量存储明文数字
        for (int i = 0; i < n; ++i) {
            data.add(scanner.nextInt()); // 读取每个明文数字
        }

        m = scanner.nextInt(); // 读取密码本的尺寸
        book = new ArrayList<>(m); // 初始化密码本向量
        for (int i = 0; i < m; ++i) {
            List<Integer> row = new ArrayList<>();
            for (int j = 0; j < m; ++j) {
                row.add(scanner.nextInt()); // 填充密码本内容
            }
            book.add(row);
        }

        directions.add(List.of(0, 1));
        directions.add(List.of(1, 0));
        directions.add(List.of(-1, 0));
        directions.add(List.of(0, -1));

        List<List<Boolean>> visited = new ArrayList<>(m); // 标记密码本中的数字是否已经被访问过
        for (int i = 0; i < m; ++i) {
            List<Boolean> row = new ArrayList<>(m);
            for (int j = 0; j < m; ++j) {
                row.add(false);
            }
            visited.add(row);
        }

        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < m; ++j) {
                if (book.get(i).get(j).equals(data.get(0))) { // 从找到的第一个数字开始搜索
                    dfs(data, 0, i, j, visited, ""); // 使用深度优先搜索找到所有可能的加密路径
                }
            }
        }

        System.out.println(found ? minPath : "error"); // 如果找到至少一种加密方式,输出最小字典序的密文;否则,输出"error"
    }

    private static void dfs(List<Integer> data, int index, int x, int y, List<List<Boolean>> visited, String path) {
        if (index == n) { // 如果已经处理完所有明文数字
            if (!found || path.compareTo(minPath) < 0) { // 如果找到的是第一种加密方式,或者字典序比之前的小
                minPath = path; // 更新最小字典序密文路径
            }
            found = true; // 标记找到了加密方式
            return;
        }

        if (x < 0 || y < 0 || x >= m || y >= m || visited.get(x).get(y) || !book.get(x).get(y).equals(data.get(index))) {
            // 如果坐标越界,或该位置已访问,或该位置数字与明文不匹配,则返回
            return;
        }

        visited.get(x).set(y, true); // 标记当前位置已访问
        String newPath = path + x + " " + y + " "; // 更新路径字符串

        // 遍历四个方向
        for (List<Integer> dir : directions) {
            int newX = x + dir.get(0);
            int newY = y + dir.get(1);
            dfs(data, index + 1, newX, newY, visited, newPath); // 在新方向上搜索下一个明文数字
        }

        visited.get(x).set(y, false); // 回溯,撤销当前位置的访问标记
    }
}
最后修改:2024 年 04 月 08 日
如果觉得我的文章对你有用,请随意赞赏