
这道题是属于回溯类型的题目。
这里我们们首先创建四个方向上的静态常量数组。
遍历所有格子的行和列,内部通过一个递归函数来查找,满足if条件就是查找到了相应的单词,返回true,否则返回false。
dfs递归函数是如何去做的呢?
先介绍一下参数列表:i是行,j是列,k是word的第k个字母,board就是棋盘上的字母,word就是需要我们查找的字母。
进入dfs后先找到第一个成功的字母,第一个不成功匹配的话直接就返回false了。直到k是word的最后一个字母时,经过第一个if的比对成功,就该返回true了。每访问一个结点的话就把它标记为访问过(也就是将其置为0的操作),避免节点走了回头路。
再通过一个for循环不断变化x,y的值相当于到达一个节点之后在四个方向上移动它去比较。if判断x,y的值一定是在合法的区间内,当前字母找到之后再让k+1去寻找下一个节点。
当前节点没找到的话,以为我们已经将其置为0了,必须要恢复现场。
DFS 是尝试所有可能路径的:
- 你从 A 出发走了一条路,发现走不通
- 你必须退回来,把 A 恢复成原来的字符
- 不然其他路径想经过 A 时,会发现 A 是 0,就误以为不能走了
java
class Solution {
private static final int[][] DIRS = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
public boolean exist(char[][] board, String word) {
char[] w = word.toCharArray();
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if (dfs(i, j, 0, board, w)) {
return true; // 搜到了!
}
}
}
return false; // 没搜到
}
private boolean dfs(int i, int j, int k, char[][] board, char[] word) {
if (board[i][j] != word[k]) { // 匹配失败
return false;
}
if (k == word.length - 1) { // 匹配成功!
return true;
}
board[i][j] = 0; // 标记访问过
for (int[] d : DIRS) {
int x = i + d[0];
int y = j + d[1]; // 相邻格子
if (0 <= x && x < board.length && 0 <= y && y < board[x].length && dfs(x, y, k + 1, board, word)) {
return true; // 搜到了!
}
}
board[i][j] = word[k]; // 恢复现场
return false; // 没搜到
}
}
优化点:假设我们要找的word里面有两个字母C,但是board里面只有一个C,那么我们就不需要再遍历了,可以直接返回false.
并且
如果 word=abcd 但 board 中的 a 很多,d 很少(比如只有一个),那么从 d 开始搜索,能更快地找到答案。(即使我们肉眼去找,这种方法也是更快的)
设 word 的第一个字母在 board 中出现了 x 次,word 的最后一个字母在 board 中出现了 y 次。
如果 y<x,我们可以把 word 反转,相当于从 word 的最后一个字母开始搜索,这样更容易在一开始就满足 board[i][j] != word[k],不会往下递归,递归的总次数更少。
优化后的代码如下:
java
class Solution {
private static final int[][] DIRS = {{0,-1},{0,1},{-1,0},{1,0}};
public boolean exist(char[][] board, String word) {
int[] cnt = new int[128];
for(char[] row : board){
for(char c : row){
cnt[c] ++;
}
}
char[] w = word.toCharArray();
int[] wordCnt = new int[128];
//优化点一 判断个数
for(char c : w){
if(++wordCnt[c] > cnt[c]){
return false;
}
}
//优化点二 看word开头字母出现的次数多还是结尾字母,结尾多直接反转word
if(cnt[w[w.length-1]] < cnt[0]){
w = new StringBuilder(word).reverse().toString().toCharArray();
}
for(int i =0;i < board.length;i++){
for(int j = 0;j < board[i].length; j++){
if(dfs(i,j,0,board,w)){
return true;
}
}
}
return false;
}
private boolean dfs(int i, int j, int k, char[][] board, char[] word){
if(board[i][j] != word[k]){
return false;
}
if(k == word.length -1){
return true;
}
board[i][j] = 0;
for(int[] d :DIRS){
int x = i + d[0];
int y = j + d[1];
if(0<= x && x < board.length && 0<= y && y < board[x].length && dfs(x,y,k+1,board,word)){
return true;
}
}
board[i][j] = word[k];
return false;
}
}
参考:
作者:灵茶山艾府