Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
For example,
Given board =
[ ['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E'] ]word =
"ABCCED"
, -> returns
true
,
word =
"SEE"
, -> returns
true
,
word = "ABCB"
, -> returns false
.
题意就是说给一个二维char数组,找出一个单词能否由这个数组中字符连线构成,连线的话只能连上下左右相邻的字母,并且一个字母只能用一次。典型的回溯问题。
虽然问题难度是中等,但是自己却遇到不少坑。一是没太明白回溯和递归的区别和联系,导致在递归处返回,以至于后面的代码压根没执行。二是忘记了回溯进一个分支前要保存当前状态,从这个分支出来后要恢复至原先的状态以便进入下一个分支。就算这样最后代码还是在最后一个比较复杂的测试用例上超时了,即使思路正确。估计是判断过于繁琐了。总之这个题和这个题的hard难度的变种都很有意思。
在贴代码之前先贴上我感觉非常精辟的关于递归和回溯的总结:
回溯和所谓的DFS(深度优先搜索)是一样的。那现在的关键是,怎么实现搜索呢?回溯既然一般使用递归来实现,那个这个递归调用该如何来写呢?我现在的理解就是,进行回溯搜索都会有一系列的步骤,每一步都会进行一些查找。而每一步的情况除了输入会不一样之外,其他的情况都是一致的。这就刚好满足了递归调用的需求。通过把递归结束的条件设置到搜索的最后一步,就可以借用递归的特性来回溯了。因为合法的递归调用总是要回到它的上一层调用的,那么在回溯搜索中,回到上一层调用就是回到了前一个步骤。当在当前步骤找不到一种符合条件情况时,那么后续情况也就不用考虑了,所以就让递归调用返回上一层(也就是回到前一个步骤)找一个以前没有尝试过的情况继续进行。当然有时候为了能够正常的进行继续搜索,需要恢复以前的调用环境。
上面的总结我们只要注意两点就行:1、返回在递归结束后。2、恢复以前的调用环境。
此题实现代码如下(AC版):
public class Solution {
public boolean exist(char[][] board, String word) {
if (word == null || word.length() == 0) {
return true;
}
char[] chs = word.toCharArray();
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if(dfs(board, chs, 0, i, j)) {
return true;
}
}
}
return false;
}
private boolean dfs(char[][] board, char[] words, int idx, int x, int y) {
if (idx == words.length) {
return true;
}
if (x < 0 || x == board.length || y < 0 || y == board[0].length) {
return false;
}
if (board[x][y] != words[idx]) {
return false;
}
board[x][y] ^= 256;
boolean exist = dfs(board, words, idx + 1, x, y + 1) ||
dfs(board, words, idx + 1, x, y - 1) || dfs(board, words, idx + 1, x + 1, y) ||
dfs(board, words, idx + 1, x - 1, y) ;
board[x][y] ^= 256;
return exist;
}
}
还有自己的最后一个用例超时版:
public class Test {
public static void main(String[] args) {
char[][] board = new char[][] { { 'a', 'b', 'c', 'e' },
{ 's', 'f', 'e', 's' }, { 'a', 'd', 'e', 'e' } };
//char[][] board = new char[][] { { 'a', 'b' } };
String s = "abceseedasf";
System.out.println(exist(board, s));
}
public static boolean exist(char[][] board, String word) {
char[] words = word.toCharArray();
int[] flag = new int[board.length * board[0].length];
char[] letter = new char[board.length * board[0].length];
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
letter[j + i * board[0].length] = board[i][j];
}
}
boolean flags = false;
for (int i = 0; i < letter.length; i++) {
if (letter[i] == words[0]) {
for (int k = 0; k < flag.length; k++) {
flag[k] = 0;
}
flag[i] = 1;
flags = findBackTracking(letter, i, words, flag,
board[0].length, 1);
if (flags) {
return flags;
}
}
}
return flags;
}
//最后一个测试用例超时,思路是正确的
public static boolean findBackTracking(char[] letter, int startIndex,
char[] words, int[] flag, int boardlength, int index) {
if (index == words.length) {
return true;
}
int nowIndex = startIndex;
boolean flag1 = false, flag2 = false, flag3 = false, flag4 = false;
if ((nowIndex - 1 >= 0 && nowIndex - 1 < flag.length)
&& (nowIndex) % boardlength != 0 && flag[nowIndex - 1] == 0) {
if (letter[nowIndex - 1] == words[index]) {
flag[nowIndex - 1] = 1;
flag1 = findBackTracking(letter, nowIndex - 1, words, flag,
boardlength, index + 1);
flag[nowIndex - 1] = 0;
}
}
if ((nowIndex + 1 >= 0 && nowIndex + 1 < flag.length)
&& (nowIndex + 1) % boardlength != 0 && flag[nowIndex + 1] == 0) {
if (letter[nowIndex + 1] == words[index]) {
flag[nowIndex + 1] = 1;
flag2 = findBackTracking(letter, nowIndex + 1, words, flag,
boardlength, index + 1);
flag[nowIndex + 1] = 0;
}
}
if ((nowIndex - boardlength >= 0 && nowIndex - boardlength < flag.length)
&& flag[nowIndex - boardlength] == 0) {
if (letter[nowIndex - boardlength] == words[index]) {
flag[nowIndex - boardlength] = 1;
flag3 = findBackTracking(letter, nowIndex - boardlength, words,
flag, boardlength, index + 1);
flag[nowIndex - boardlength] = 0;
}
}
if ((nowIndex + boardlength >= 0 && nowIndex + boardlength < flag.length)
&& flag[nowIndex + boardlength] == 0) {
if (letter[nowIndex + boardlength] == words[index]) {
flag[nowIndex + boardlength] = 1;
flag4 = findBackTracking(letter, nowIndex + boardlength, words,
flag, boardlength, index + 1);
flag[nowIndex + boardlength] = 0;
}
}
return flag1 || flag2 || flag3 || flag4;
}
}