单词搜索
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例1:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
DFS + 回溯
思路:
我们已经确定使用 回溯 那么要回溯哪里,任何实现呢?
1、遍历数组,找到和 word 第一个字符相同的元素 (i,j),标志该元素已使用,找到了第一个,那么我们应该找第二个,最佳的方法就是向该元素的四周探寻,判断该元素的四周是否存在 word 的下一个字符。
2、我们知道了 入口 (i,j)使用递归来探寻(i,j)四周(上下左右)是否存在 word 的下一个字符,存在继续查找下一个,不存在直接返回,
3、综合上述可以得出:
①:遍历 board ,得到 (i,j)
②:递归 (i,j)的四个方向(上下左右),前提是 (i,j)包含 word 中第一个字符
③:记录已经比较过的字符,可以使用一个 boolean 二维数组或者在 board 中修改
④:递归完一个 (i,j)记得回溯,不然第一个(i,j)不符合,判断第二个的时候就会受影响。回溯的是 我们标记的 boolean 二维数组,或者 修改过的 board 也就是 ② 中标记的元素。
代码:
public static boolean exist(char[][] board, String word) {
//回溯,让我们矩阵上的 i,j 去探索其 上下左右,是否村咋,不存在下一个 i,j.不断探索,直到完成,使用过的单词标记
int h = board.length, w = board[0].length;
boolean[][] visited = new boolean[h][w];
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
if (check(board, visited, i, j, word, 0)) {
return true;
}
}
}
return false;
}
public static boolean check(char[][] board, boolean[][] visited, int i, int j, String s, int k) {
//是否存在,不存在直接返回false
if (board[i][j] != s.charAt(k)) {
return false;
//k == s.length() -1 表示已经查找完毕,并且无 false,结束,直接返回 ture
} else if (k == s.length() - 1) {
return true;
}
visited[i][j] = true;
//上下左右四个行走方向
int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
boolean result = false;
for (int[] dir : directions) {
//定义下一个探索的方向,i,j
int newi = i + dir[0], newj = j + dir[1];
//判断是否超出边界
if (newi >= 0 && newi < board.length && newj >= 0 && newj < board[0].length) {
//是否已经标记使用过该单词
if (!visited[newi][newj]) {
//进行下一个方向的查找,下一个单词,所以 k+1,方向 为 newi,newj
boolean flag = check(board, visited, newi, newj, s, k + 1);
//如果 找到下一个,结果设置为 ture 跳过当前循环,继续查找下一个
if (flag) {
result = true;
break;
}
}
}
}
//回溯,这个 i,j的上下左右不存在,可能下一个 i,j 的上下左右存在呢。
visited[i][j] = false;
return result;
}
在原来数组中修改:
public boolean exist(char[][] board, String word) {
//回溯,让我们矩阵上的 i,j 去探索其 上下左右,是否村咋,不存在下一个 i,j.不断探索,直到完成,使用过的单词标记
int h = board.length, w = board[0].length;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
if (check(board, i, j, word, 0)) {
return true;
}
}
}
return false;
}
public static boolean check(char[][] board, int i, int j, String s, int k) {
//判断是否存在,是否使用过,是否越界
if ( i < 0 || i > board.length - 1 || j < 0 || j > board[0].length - 1 || board[i][j] == '.' || board[i][j] != s.charAt(k)) {
return false;
//k == s.length() -1 表示已经查找完毕,并且无 false,结束,直接返回 ture
}
if (k == s.length() - 1) {
return true;
}else {
char temp = board[i][j];
//标记是否使用过
board[i][j] = '.';
//结合四个方向的判断结果,其中一个为 true 那么说明有匹配
boolean res = check(board,i,j+1,s,k+1) || check(board,i,j-1,s,k+1) || check(board,i+1,j,s,k+1) || check(board,i-1,j,s,k+1);
//回溯
board[i][j] = temp;
return res;
}
}