079J Word Search
https://leetcode.com/problems/word-search/
这是一个很不一样的回溯。我直觉是 dfs。但是发现应当回溯。 我的答案逻辑是,首先找到一个开始的点。然后 dfs。如果 dfs 每前进一层,能够匹配下一个字符。那么我们就 return true. 反之就 return false. 现在有问题了: dfs 是需要毁灭性的 标记访问路径的。如果一个起点 return false,那么我们需要 想办法退回去的。朴素的思路就是,如果四个方向皆碰壁,那么就 意味着没有一条路是通顺的。此时我们应当回退。
这个题之所以写的比起典型的回溯要不太一样。是因为典型的回溯的图一定需要遍历 全局。而这个题并不需要。我们只需要尝试全部的起点就可以了。
class Solution {
private int L; // word.length()
private int M;
private int N;
public boolean exist(char[][] board, String word) {
this.L = word.length();
// if(word.length == 0) return true;
this.M = board.length;
if(M < 1) return false;
this.N = board[0].length;
for(int i = 0; i < M; i++){
for(int j = 0; j < N; j++){
if(dfs(board, word, i, j, 0)){
return true;
}
}
}
return false;
}
private boolean dfs(char[][] board, String word, int row, int column, int index){
if(index == L) return true;
if( (row < 0) || (row > M - 1) || (column < 0) || (column > N - 1 )) {
return false;
}
if( board[row][column] != word.charAt(index) ) return false;
char curChar = board[row][column];
board[row][column] = '.';
if ( dfs(board,word, row + 1, column, index+1) ||
dfs(board,word, row - 1, column, index+1) ||
dfs(board,word, row, column + 1, index+1) ||
dfs(board,word, row, column - 1, index+1) ){
return true;
}
board[row][column] = curChar;
return false;
}
}
注意,上面的写法会有更好的时间复杂度,因为下面的写法,虽然更符合直觉,但是无论什么情况都会 dfs 到最后一层,所以会超时。
class Solution {
private final int[][] DIRECTIONS = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
public boolean exist(char[][] board, String word) {
for(int i = 0; i < board.length; i++){
for(int j = 0; j < board[0].length; j++){
if(backtrack(board, new boolean[board.length][ board[0].length], word, new StringBuilder(), i, j, 0)){
return true;
}
}
}
return false;
}
private boolean backtrack(char[][] board, boolean[][] marked, String word, StringBuilder sb, int row, int col, int level){
if(level >= word.length() ) {
return word.equals(sb.toString());
}
if(row < 0 || col < 0 || row >= board.length || col >= board[0].length) return false;
if(marked[row][col]) return false;
marked[row][col] = true;
sb.append(board[row][col]);
for(int[] direction : DIRECTIONS){
if(backtrack(board, marked, word, sb, row + direction[0], col + direction[1], level + 1)) return true;
}
sb.deleteCharAt(sb.length() - 1);
marked[row][col] = false;
return false;
}
}
综合起来给出下面的写法。
class Solution {
private int L; // word.length()
private int M;
private int N;
private final int[][] DIRECTIONS = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
public boolean exist(char[][] board, String word) {
this.L = word.length();
// if(word.length == 0) return true;
this.M = board.length;
if(M < 1) return false;
this.N = board[0].length;
for(int i = 0; i < M; i++){
for(int j = 0; j < N; j++){
if(backtrack(board, word, i, j, 0)){
return true;
}
}
}
return false;
}
private boolean backtrack(char[][] board, String word, int row, int column, int index){
if(index == L) return true;
if( (row < 0) || (row > M - 1) || (column < 0) || (column > N - 1 )) {
return false;
}
if( board[row][column] != word.charAt(index) ) return false;
char curChar = board[row][column];
board[row][column] = '.';
for(int[] direction : DIRECTIONS){
if(backtrack(board, word, row + direction[0], column + direction[1], index + 1)) return true;
}
board[row][column] = curChar;
return false;
}
}
下面的解法基于212的Trie的解法,果然很快。。。比起上面的是 (117ms < 170ms>)
class Solution {
class TrieNode {
TrieNode[] children;
boolean isWord;
String word;
public TrieNode() {
this.children = new TrieNode[128];
this.isWord = false;
this.word = "";
}
}
private TrieNode root;
private char[][] board;
private int M;
private int N;
private boolean[][] marked;
private boolean hasWord;
private int[] directions = new int[]{1, 0, -1, 0, 1};
public boolean exist(char[][] board, String word) {
this.board = board;
this.M = board.length;
this.N = board[0].length;
this.hasWord = false;
this.root = new TrieNode();
buildTrie(word);
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
if (hasWord) return hasWord;
if (root.children[board[i][j] - 'A'] == null) continue;
TrieNode p = root.children[board[i][j] - 'A'];
marked = new boolean[M][N];
marked[i][j] = true;
dfs(i, j, p);
}
}
return hasWord;
}
private void dfs(int row, int col, TrieNode p) {
if (hasWord) return;
if (p.isWord) hasWord = true;
for (int i = 0; i < 4; i++) {
int nextRow = row + directions[i];
int nextCol = col + directions[i + 1];
if (nextRow < 0 || nextCol < 0 || nextRow >= M || nextCol >= N) continue;
if (marked[nextRow][nextCol]) continue;
char targetChar = board[nextRow][nextCol];
if (p.children[targetChar - 'A'] == null) continue;
marked[nextRow][nextCol] = true;
dfs(nextRow, nextCol, p.children[targetChar - 'A']);
marked[nextRow][nextCol] = false;
}
}
private void buildTrie(String word) {
TrieNode p = root;
for (int i = 0; i < word.length(); i++) {
char cur = word.charAt(i);
if (p.children[cur - 'A'] == null) {
p.children[cur - 'A'] = new TrieNode();
}
p = p.children[cur - 'A'];
if (i == word.length() - 1) {
p.isWord = true;
p.word = word;
}
}
}
}
Last updated
Was this helpful?