题目描述

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

规则如下:

  1. 明文为一段数字串由 0~9 组成

  2. 密码本为数字 0~9 组成的二维数组

  3. 需要按明文串的数字顺序在密码本里找到同样的数字串,密码本里的数字串是由相邻的单元格数字组成,上下和左右是相邻的,注意:对角线不相邻,同一个单元格的数字不能重复使用。

  4. 每一位明文对应密文即为密码本中找到的单元格所在的行和列序号(序号从0开始)组成的两个数宇。

    如明文第 i 位 Data[i] 对应密码本单元格为 Book[x][y],则明文第 i 位对应的密文为X Y,X和Y之间用空格隔开。

如果有多条密文,返回字符序最小的密文。

如果密码本无法匹配,返回"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
说明
输入2
0 5
3
0 0 2
1 3 4
6 6 4
输出error
说明找不到 0 5 的序列,返回error
解题思路

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

  1. 初始化变量
    • n 和 m 分别存储明文的长度和密码本的尺寸。
    • book 二维数组存储密码本内容。
    • directions 数组表示搜索的四个方向(右、下、左、上)。
    • minPath 字符串用于存储找到的字典序最小的密文路径。
    • found 布尔变量标记是否找到至少一种加密方式。
  2. 搜索准备
    • 创建一个 visited 布尔二维数组来标记密码本中的数字是否已被访问,以避免重复搜索。
  3. 开始搜索
    • 遍历密码本的每个数字,当找到一个数字与明文的第一个数字相匹配时,从该位置开始使用深度优先搜索(DFS)。
  4. 深度优先搜索(DFS)
    • 递归地搜索所有可能的路径。对于当前位置,如果满足以下条件之一,则继续搜索:
      • 当前位置的数字与明文的当前索引指向的数字相匹配。
      • 已到达明文的末尾,且路径符合条件(字典序最小或找到的第一条路径)。
    • 在每个位置,尝试向四个方向移动,并递归调用 dfs 方法继续搜索。
    • 搜索过程中,使用 visited 数组标记当前位置已访问,以避免循环访问。
  5. 回溯与更新
    • 每当找到一条完整的匹配路径时,比较并更新 minPath 为字典序最小的路径。
    • 完成当前路径的搜索后,回溯(撤销当前位置的访问标记),尝试其他可能的路径。
  6. 输出结果
    • 如果找到至少一条匹配的路径(即 found 为 true),则输出字典序最小的密文路径。
    • 否则,输出 "error" 表示无法在密码本中找到与明文完全匹配的路径。
代码实现
import java.util.*;

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

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt(); // 读取明文的长度
        int[] data = new int[n]; // 创建数组存储明文数字
        for (int i = 0; i < n; i++) {
            data[i] = scanner.nextInt(); // 读取每个明文数字
        }

        m = scanner.nextInt(); // 读取密码本的尺寸
        book = new int[m][m]; // 初始化密码本数组
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < m; j++) {
                book[i][j] = scanner.nextInt(); // 填充密码本内容
            }
        }

        boolean[][] visited = new boolean[m][m]; // 标记密码本中的数字是否已经被访问过
        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, ""); // 使用深度优先搜索找到所有可能的加密路径
                }
            }
        }

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

    /*
     * 深度搜索明文的加密路径
     * data    明文数组
     * index   遍历次数(明文长度)
     * x       密码本的行号
     * y       密码本的列号
     * visited 密码本中的数字访问数组
     * path    密文路径
     */
    public static void dfs(int[] data, int index, int x, int y, 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[x][y] || book[x][y] != data[index]) {
            // 如果坐标越界,或该位置已访问,或该位置数字与明文不匹配,则返回
            return;
        }

        visited[x][y] = true; // 标记当前位置已访问
        String newPath = path + x + " " + y + " "; // 更新路径字符串

        if (index == n - 1 || book[x][y] == data[index]) {
            dfs(data, index + 1, x, y, visited, newPath); // 继续搜索下一个明文数字
        }

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

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

更多推荐