解法一:
1、思路
(回溯) O(n2·3k)
深度优先搜索,我们定义这样一种搜索顺序,即先枚举单词的起点,然后依次枚举单词的每个字母。在这个过程中需要将已经使用过的字母改成一个特殊字母,以避免重复使用字符。
递归函数设计:
boolean dfs(char[][] board, String word, int u, int x, int y)
u
代表当前枚举到了目标单词word的
第u
个位置。
x
,y
是当前搜索到的二维字符网格的横纵坐标。
搜索过程如下:
1、在二维字符网格中枚举每个单词的起点。
2、从该起点出发向四周搜索单词word,并记录此时枚举到单词word的第u个位置 ( u从0开始)。
3、如果当前搜索的位置(x,y)的元素board[x][y] == word[u],则继续向四周搜索。
4、直到枚举到单词word的最后一个字母返回ture,否则返回false。
递归边界:
1、当搜索过程出现当前位置board[x][y] != word[u] ,说明当前路径不合法,返回false。
2、u == word.size() - 1,成功搜索到单词末尾,返回true。
实现细节:
1、从搜索过的位置继续搜索下一层时,需要对当前位置进行标识,表示已经搜索
2、可以使用偏移数组来简化代码。
时间复杂度分析: 单词起点一共有 n2个,单词的每个字母一共有上下左右四个方向可以选择,但由于不能走回头路,所以除了单词首字母外,仅有三种选择。所以总时间复杂度是 O(n2·3k)。
2、参考java代码 :
class Solution {
public boolean exist(char[][] board, String word) {
// 对二维字符网格中的每个元素枚举单词起点 i纵j横
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if (dfs(board, word, 0, i, j)) {
return true;
}
}
}
// 全部无法匹配自然就是失败
return false;
}
// 存放几个偏移量,方便修改x,y坐标向四周搜索
int[] dx = new int[] { -1, 0, 1, 0 };
int[] dy = new int[] { 0, 1, 0, -1 };
// 题设数组、题设单词、枚举到了word的第几个位置、xy为当然搜索的二维字符网格横纵坐标
boolean dfs(char[][] board, String word, int u, int x, int y) {
if (board[x][y] != word.charAt(u))// 递归边界1
{
return false;
} // 如果没有匹配到word第u个字符,直接退出,不符题意
if (u == word.length() - 1) {// 递归边界2
return true;// 搜索到单词末尾,完成目标,返回ture
}
char char_temp = board[x][y];// 临时存一下
board[x][y] = '.';// 这里随意,保证前后一致即可,主要是改成特殊字母避免重复搜索
for (int i = 0; i < 4; i++) {// board[x][y]==word.charAt(u) 为真,且没有到边界,向四周搜索
int a = x + dx[i]; // 加减一些偏移量实现四周搜索,如i=1时,a、b的值是x-1、y不变,即向左搜索
int b = y + dy[i];
if (a < 0 || a >= board.length || b < 0 || b >= board[0].length || board[a][b] == '.') {
continue; // 当到达搜索边界或者找到了已经搜过的字符,换一个位置重新搜
}
if (dfs(board, word, u + 1, a, b)) {
return true; // 如果没有到上个if检测的检测的边界或者已经搜索过的,则对word的下一个字符进行dfs搜索递归
}
}
// 回溯,将填充的.变回去
board[x][y] = char_temp;
return false;
}
}
解法二:(感觉该回溯解法更易于理解)
public class Solution {
// 判断给定的单词是否存在于二维字符网格中
public boolean exist(char[][] board, String word) {
// 如果网格或者单词为空,则返回false
if (board == null || board.length == 0 || board[0].length == 0 || word == null || word.length() == 0) {
return false;
}
int m = board.length; //网格纵向长度
int n = board[0].length; //网格横向长度
boolean[][] visited = new boolean[m][n]; // 记录每个位置是否已经访问过
// 遍历二维字符网格,对每个格子调用backtrack方法进行回溯搜索
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (backtrack(board, visited, word, 0, i, j)) {
return true;
}
}
}
return false; // 遍历完整个网格后仍未找到匹配的单词,返回false
}
// 回溯搜索匹配单词
private boolean backtrack(char[][] board, boolean[][] visited, String word, int index, int i, int j) {
// 如果单词匹配完成,返回true
if (index == word.length()) {
return true;
}
// 判断当前位置是否越界、是否已经访问过、字符是否匹配等情况(特殊情况先排除,再处理)
if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || visited[i][j]
|| board[i][j] != word.charAt(index)) {
return false;
}
visited[i][j] = true; // 标记当前位置已经访问过
// 依次尝试向上、向下、向左、向右四个方向进行搜索,直到匹配完成或者无法继续搜索为止
boolean found = backtrack(board, visited, word, index + 1, i + 1, j) || // 向下搜索
backtrack(board, visited, word, index + 1, i - 1, j) || // 向上搜索
backtrack(board, visited, word, index + 1, i, j + 1) || // 向右搜索
backtrack(board, visited, word, index + 1, i, j - 1); // 向左搜索
visited[i][j] = false; // 回溯,重置访问状态
return found; // 返回搜索结果
}
}