剑指 Offer 12.矩阵中的路径
这题就是深度优先搜索加剪枝(搜到不是目标字符就返回),在搜索到正确字符时要记得return true,否则就会继续for循环,一直返回false。
基础版:
class Solution {
int[][] next = new int[][]{{0,1},{1,0},{0,-1},{-1,0}};
int[][] book;
public boolean exist(char[][] board, String word) {
book = new int[board.length][board[0].length];
for(int i = 0; i < board.length; i++)
for(int j = 0; j < board[0].length; j++) {
book[i][j] = 0;
}
for(int i = 0; i < board.length; i++)
for(int j = 0; j < board[0].length; j++) {
if(board[i][j] == word.charAt(0)) {
book[i][j] = 1;
if(dfs(board, book, word, i, j, 1)) return true;
book[i][j] = 0;
}
}
return false;
}
public boolean dfs(char[][] board, int[][] book, String word,int x, int y, int step) {
int tx,ty,k;
if(step == word.length()) return true;
for(k = 0; k <= 3; k++) {
tx = x + next[k][0];
ty = y + next[k][1];
if(tx < 0 || tx > board.length - 1 || ty < 0 || ty > board[0].length - 1 || board[tx][ty] != word.charAt(step) ) continue;
if(book[tx][ty] == 0) {
book[tx][ty] = 1;
if(dfs(board, book, word, tx, ty, step+1)) return true;
book[tx][ty] = 0;
}
}
return false;
}
}
改进版:
可以不使用book数组来标记已访问过的点,将board[x][y](已访问过的点)改为’\0’,它将不会和任何字符相等,回溯时再将它恢复成原来的字符word.charAt(step-1)。step-1为已比对过的字符。step为将要比对的字符。
class Solution {
int[][] next = new int[][]{{0,1},{1,0},{0,-1},{-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(board[i][j] == word.charAt(0)) {
if(dfs(board, word, i, j, 1)) return true;
}
}
return false;
}
public boolean dfs(char[][] board, String word,int x, int y, int step) {
int tx,ty,k;
if(step == word.length()) return true;
board[x][y] = '\0';
for(k = 0; k <= 3; k++) {
tx = x + next[k][0];
ty = y + next[k][1];
if(tx < 0 || tx > board.length - 1 || ty < 0 || ty > board[0].length - 1 || board[tx][ty] != word.charAt(step) ) continue;
if(dfs(board, word, tx, ty, step+1)) return true;
}
board[x][y] = word.charAt(step-1);
return false;
}
}
时间复杂度O(3^k*MN)
: 最差情况下,需要遍历矩阵中长度为 KK字符串的所有方案,时间复杂度为 O(3^K),矩阵中共有 MN个起点,时间复杂度为 O(MN) 。
方案数计算: 设字符串长度为 K ,搜索中每个字符有上、下、左、右四个方向可以选择,舍弃回头(上个字符)的方向,剩下 3 种选择,因此方案数的复杂度为 O(3^K)。
空间复杂度 O(K) : 搜索过程中的递归深度不超过 K。
二刷
class Solution {
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((board[i][j] == word.charAt(0))) {
if(dfs(i, j, board, 0, word))
return true;
}
}
return false;
}
public boolean dfs(int i, int j, char[][] board, int word_index, String word) {
if(i < 0 || j < 0 || i >= board.length || j >= board[0].length || board[i][j] != word.charAt(word_index)) return false;
if(word_index == word.length() - 1) return true;
char temp = board[i][j];
board[i][j] = '\0';
boolean res = dfs(i, j + 1, board, word_index + 1, word) || dfs(i + 1, j, board, word_index + 1, word) ||
dfs(i, j - 1, board, word_index + 1, word) || dfs(i - 1, j, board, word_index + 1, word);
board[i][j] = temp;
return res;
}
}
改用或的形式