文章摘要:
- 题目要求在给定的二维字符矩阵中查找是否存在指定的字符串,只能通过相邻方格(上下左右)且不能重复使用同一方格。采用深度优先搜索(DFS)策略,通过回溯法遍历所有可能的路径。 算法核心步骤: 遍历矩阵,找到与目标字符串首字符匹配的位置 使用DFS从该位置开始搜索后续字符 维护访问标记数组避免重复使用 通过方向数组处理四个相邻位置 找到完整字符串返回true,否则回溯并继续搜索 时间复杂度:O(mn*3^L),其中mn为矩阵大小,L为字符串长度。空间复杂度:O(mn)用于存储访问标记。
文章目录
一、题目解析
题目给出一个 m x n 的二维字符矩阵 board,并且给出一个字符串 word,我们需要从这个二位字符矩阵中找是否存在 字符串 word,若存在,返回 true,否则返回 false。
这里需要注意,只能是相邻 的方格,不可以斜着找,并且每一个方格只能找一次而不能重复使用。
例如,题目给出:
- board = [ [ 'A', 'B', 'C', 'E' ], [ 'S', 'F', 'C', 'S' ], [ 'A', 'D', 'E', 'E' ] ], word = "ABCCED",
则 board 是如下所示的一个矩阵:
| A | B | C | E |
|---|---|---|---|
| S | F | C | S |
| A | D | E | E |
我们需要从这个矩阵中找到字符串 ABCCED,如下所示:
A |
B |
C |
E |
|---|---|---|---|
| S | F | C |
S |
| A | D |
E |
E |
因为可以在矩阵中找到对应的字符串,所以返回 true。
若 word = "SEE",也可以找到:
| A | B | C | E |
|---|---|---|---|
| S | F | C | S |
| A | D | E |
E |
此时也是返回 true。
若 word = "ABCB",则无法找到,返回 false。
| A | B | C | E |
|---|---|---|---|
| S | F | C | S |
| A | D | E | E |
二、算法原理 + 代码实现
我们这里采用暴搜的策略,每一个可能的路径都搜索出来,若遇到存在的情况就返回 true,否则就返回 false。
在二维矩阵中搜索且不能重复,常常需要创建一个布尔类型的 visit 二维数组,其大小设置和 board 一样,目的是为了记录二维矩阵中的每一个方格的使用状态:若未被使用则默认是 false;若已被使用则改成 true。
决策树
基于例子:board = [ [ 'A', 'B', 'C', 'E' ], [ 'S', 'F', 'C', 'S' ], [ 'A', 'D', 'E', 'E' ] ], word = "ABCCED" 画出部分决策树如下:

大致过程如下:
- 我们首先遍历
board,找到word的第一个字符 "A" ,然后基于这个位置开始递归(使用了这个位置不要忘记将该位置在visit中的值更新为 true),递归的时候需要知道当前位置在 board 中的坐标 - 当前在第一个位置 "A" 处,我们在该位置的上下左右四个位置 找 word 的第二个字符 "B",很显然就是位置 "A" 的右边,接下来更新位置的状态后基于这个位置继续递归,以此类推,直到我们找到 word 的最后一个字符,就返回 true。
这里我们需要注意的步骤是如何在某个位置寻找上下左右呢?
一个常用的做法是定义两个向量坐标,具体原理是:

坐标 [ i, j ] 的上下左右四个坐标都是在 i 和 j 上加上了 0、1、-1 的:
- 上下坐标是 [ i + (-1), j + 0] 和 [ i + 1, j + 0 ];
- 左右坐标是 [ i + 0, j + (-1)] 和 [ i + 0, j + 1 ]。
我们定义两个向量坐标:dx = {0, 0, -1, 1},dy = {-1, 1, 0, 0}。
这样,我们循环四次就可以依次访问一个位置 [ row, col ] 的上下左右坐标,并使用 x 和 y 代替他们的坐标,其中:x = row + dx[k],y = col + dy[k](k 表示循环的次数),而 board[x][y] 也就表示这个坐标在矩阵中的位置(需要注意 x 和 y 不能越界)。
全局变量
我们可以将题目给出的 board 和 word 都改成全局变量,这样递归的时候可以不用作为参数了,方便很多(我个人认为)。这里也可以将 word 设置为字符数组,方便后续的操作。
然后需要一个大小与 board 一样的布尔数组 visit,为了遍历方便,将 board 的大小 m 和 n 也改成全局变量。
在访问某个位置的上下左右的时候需要用到两个方向数组 dx 和 dy。
java
char[][] board;
char[] word;
boolean[][] visit;
int m, n;
// 方向数组
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
dfs 函数
函数头
按照我们的思路,在递归的时候需要直到某个位置在 board 矩阵中的坐标,所以需要有两个参数 row 和 col。
同时,为了明确每一次递归所要查找字符是在 word 中的哪一个字符,需要一个下标 pos 。
这里我们需要做一个和 解数独 一样的操作:把 dfs 函数的返回值设置为布尔类型,这是因为我们需要知道本次的选择是否正确,如果正确就返回 true,不正确就返回 false。
java
boolean dfs(int row, int col, int pos)
函数体
由于 dfs 函数的参数涉及到下标 pos,我们首先要保证不越界。
然后我们循环四次,依次访问当前位置 [ row, col ] 的上下左右四个位置并判断在 board 中对应位置的字符是否是本次递归想要寻找的字符,找到之后线更新位置状态再继续递归,否则还原现场。
如果循环四次结束后仍未返回,则表示四个位置的字符都不符合,返回 false。
细节问题
回溯
本题有两个地方需要回溯:遍历 board 的时候需要回溯;dfs 函数中访问某位置的上下左右的时候也需要回溯。
具体的回溯操作就是将 visit 数组更改回 false。
剪枝
我们的解题思想是暴搜,因此不涉及到剪枝操作。
递归出口
在 dfs 函数的参数涉及到了下标 pos,因此需要保证 pos 不能越界:下标 pos 等于 word 的长度时刚好是递归出口。
代码实现
java
class Solution {
char[][] board;
char[] word;
boolean[][] visit;
int m, n;
// 方向数组
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
public boolean exist(char[][] givenBoard, String givenWord) {
board = givenBoard; word = givenWord.toCharArray();
m = board.length; n = board[0].length;
visit = new boolean[m][n];
// 遍历board
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
// 在board中找到word的第一个字符
if (board[i][j] == word[0]) {
visit[i][j] = true;
if (dfs(i, j, 1)) return true;
visit[i][j] = false;
}
}
}
return false;
}
public boolean dfs(int row, int col, int pos) {
// 递归出口,保证pos不越界
if (pos == word.length) {
return true;
}
// 从[row,col]这个位置向四个方向寻找下一个位置
for (int k = 0; k < 4; k++) {
int x = row +dx[k], y = col + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n) {
if (!visit[x][y] && board[x][y] == word[pos]) {
// 找到对应的字符
visit[x][y] = true;
if (dfs(x, y, pos + 1)) return true;
visit[x][y] = false;
}
}
}
// 到此处若还未返回,说明四个方向都没有匹配到相应字符,返回false
return false;
}
}
文章到此处就告一段落啦,若有错误请尽管指出~
完