leetcode
leetcode 3051 ~ 3100
提取咒文

提取咒文

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 1470 ms, 内存: 25.3 MB


/*
 * 思路:
 * 使用 Java Stream 进行 BFS 搜索每个字母的最短路径。
 * 在 Java Stream 中对队列进行并行处理。
 */
import java.util.*;
import java.util.stream.*;

public class Solution {
    public int minSteps(String[] matrix, String mantra) {
        int rows = matrix.length;
        int cols = matrix[0].length();
        int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

        class Node {
            int x, y, steps;
            Node(int x, int y, int steps) {
                this.x = x;
                this.y = y;
                this.steps = steps;
            }
        }

        int totalSteps = 0;
        int startX = 0, startY = 0;

        for (char c : mantra.toCharArray()) {
            Queue<Node> queue = new LinkedList<>();
            boolean[][] visited = new boolean[rows][cols];
            queue.add(new Node(startX, startY, 0));
            visited[startX][startY] = true;
            boolean found = false;

            while (!queue.isEmpty()) {
                List<Node> nodeList = new LinkedList<>();
                queue.forEach(nodeList::add);
                queue.clear();

                nodeList.parallelStream().forEach(node -> {
                    if (matrix[node.x].charAt(node.y) == c) {
                        totalSteps += node.steps;
                        startX = node.x;
                        startY = node.y;
                        found = true;
                        queue.clear();
                    }
                    for (int[] dir : directions) {
                        int newX = node.x + dir[0];
                        int newY = node.y + dir[1];
                        if (newX >= 0 && newX < rows && newY >= 0 && newY < cols && !visited[newX][newY]) {
                            queue.add(new Node(newX, newY, node.steps + 1));
                            visited[newX][newY] = true;
                        }
                    }
                });

                if (found) break;
            }

            if (!found) {
                return -1;
            }
        }
        return totalSteps + mantra.length();
    }
}

解释

方法:

该题解采用了宽度优先搜索(BFS)的策略来求解。首先,定义一个队列来存储探索状态,每个状态包含当前位置(x, y),当前正在尝试匹配的mantra中的字符索引(level),以及到达当前状态所需的步数(step)。整个矩阵的每个位置对应的每个字符索引都维护一个访问状态,以避免重复访问并陷入无限循环。从矩阵的左上角开始,对每个状态进行扩展:如果当前位置的字符与mantra中的当前字符匹配,则尝试移动到下一个字符,同时步数加一;否则,尝试向四个方向移动,每移动一步,步数也同样加一。这个过程持续进行,直到队列为空或成功匹配完所有字符。如果队列为空还未匹配完,返回-1表示无法完成提取。

时间复杂度:

O(m*n*L)

空间复杂度:

O(m*n*L)

代码细节讲解

🦆
为什么选择宽度优先搜索(BFS)而不是深度优先搜索(DFS)来解决这个问题?
宽度优先搜索(BFS)适用于这种类型的问题,因为它按层次(逐级)探索所有可能的路径,从而确保找到的第一个解决方案是最短的路径。在尝试提取咒文的过程中,我们的目标是找到最小的步数,BFS可以在找到第一个匹配完整咒文的路径时立即停止搜索,保证这是最短的可行路径。相反,深度优先搜索(DFS)会探索完一个方向的所有可能性之后才会回溯,这可能导致它找到的是一个非最优解,而且效率较低,因为它可能探索很多不必要的路径。
🦆
在题解中提到的访问状态数组,每个位置存储的是什么信息?为什么需要这样一个三维数组来记录状态?
访问状态数组是一个三维数组,每个元素代表一个特定字符在特定位置是否已经被访问过。具体地,第一维代表字符在咒文中的索引(level),第二维和第三维分别代表矩阵中的行(x)和列(y)。这样的设计是为了防止在搜索过程中重复访问相同的状态,从而避免无限循环并减少不必要的计算。通过记录每个字符索引在每个位置的访问状态,我们可以精确控制搜索过程,确保每个状态只被访问一次,提高算法效率。
🦆
如何处理位于矩阵边缘的格子,在尝试向外扩展时不越界?
为了处理位于矩阵边缘的格子并防止索引越界,题解中在尝试移动到新位置之前,会检查新的坐标(nx, ny)是否有效。具体的检查方法是确保nx和ny都在矩阵的有效范围内,即0 <= nx < m和0 <= ny < n,其中m和n分别是矩阵的行数和列数。只有当新的坐标在这个有效范围内时,才会继续执行移动和探索操作。这样的边界检查确保了算法的健壮性,避免了程序错误或崩溃。

相关问题