题目
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
样例
思路分析
本题采用DFS进行深度搜索,如果搜索到的下一个点位,与word中对应位的值一样,那么就继续进行深度搜索
我们采用dfs中携带一个n值来表示搜索到了第几位。
采用path[][] 来表示已经被使用的路径
代码
分为思考过程冗余版本,以及优化简短版本
冗余版本:每一步拆分很细致,易懂
冗余版本
package 剑指offer.搜索与回溯.offer12;
import java.util.Arrays;
public class Solution {
public static void main(String[] args) {
char[][] tests = {{'A','B','C','E'},{'S','F','C','S'},{'A','D','E','E'}};
String words = "ABCCED";
// char[][] tests = {{'a'}};
// String words = "a";
// char[][] tests = {{'a','a'}};
// String words = "aa";
// char[][] tests = {{'C','A','A'},{'A','A','A'},{'B','C','D'}};
// String words = "AAB";
Solution solution = new Solution();
boolean res = solution.exist(tests, words);
System.out.println(res);
}
/**
* 思路分析:
* 总体思路:采用dfs or bfs 进行遍历
* 使用深度搜索,就是使用n便是word[]的第几位
* step1: 使用pathWord 存放符合条件数据,word[n]与遍历到的值进行比较,还需要一个char path[][]来表示以及走过的路径,初始全0,走过表示为1
* step2:dfs下一步应该怎么走,沿着四个方向走,定义四个方向,上[-1][] 下[+1][] 左[][-1] 右[][+1] 甚至可以将移动单独抽为一个函数,并且判断是否走过重复,和是否撞墙
*step3:结束当n == word.lenth时说明,以及遍历完了word返回true,当n周围都是已经遍历过得或者null时候就表示结束了
*
*
* 代码优化:
* path[y][x]置为0可以优化掉
*
* 四个方向可以优化掉
*
* @param board
* @param word
* @return
*/
char[] words ;
char[][] boards;
char[][] path;
int height;
int width;
public boolean exist(char[][] board, String word) {
//定义路径变量
height = board.length;
width = board[0].length;
path = new char[height][width];
// //✅针对height =1的写法,将单独一行的进行比较即可
// if(height == 1){
// String oneHeight = String.valueOf(board[0]);
//
// if(oneHeight.contains(word)){
// return true;
// }
// }
int n = 0;
int x = 0;
int y = 0;
words = word.toCharArray();
boards = board;
//todo:找到与word[0]相同的数字进行开局
//✅存在问题,遍历寻找需要每一个可能的都需要进行dfs
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if(boards[i][j] == words[0]){
y = i;
x = j;
if(words.length == 1){
return true;
}
if(dfs(n,x,y)){
return true;
}
}
}
}
//[y][x]
return false;
}
/**
*
* @param n 表示遍历了几层,表示与words[n]相对应,当n == len时,我已经把words遍历完了
* @param x
* @param y
* @return
*/
public boolean dfs(int n,int x,int y){
//判断当前是否合适
if(!(boards[y][x] == words[n])){
return false;
}
//✅需要先判断是否合适,然后n与len-1进行比较
if(n == words.length-1){
return true;
}
//todo:将x,y列为已经可以走的
path[y][x] = 1;
//向上移动
boolean canMove = canMove(x, y-1);
if(canMove){
boolean resPart1 = dfs(n+1,x,y-1);
if(resPart1){
return resPart1;
}
}
//向右移动
canMove = canMove(x+1, y);
if(canMove){
boolean resPart2 = dfs(n+1,x+1,y);
if(resPart2){
return resPart2;
}
}
//向下移动
canMove = canMove(x, y+1);
if(canMove){
boolean resPart3 = dfs(n+1,x,y+1);
if(resPart3){
return resPart3;
}
}
//向左移动
canMove = canMove(x-1, y);
if(canMove){
boolean resPart4 = dfs(n+1,x-1,y);
if(resPart4){
return resPart4;
}
}
//只需要在dfs结束的时候将自己走过的放为0,每一个return false的地方置为0即可
path[y][x] = 0;
return false;
}
//判断能否这样移动,判断是否越界,判断是否已经在路径中
public boolean canMove(int x,int y){
//判断是否越界
if(x < 0){
return false;
}
if(x >= width){
return false;
}
if(y < 0){
return false;
}
if(y >= height){
return false;
}
//判断是否已经走过
if(path[y][x] == 1){
return false;
}
return true;
}
}
优化版本
package 剑指offer.搜索与回溯.offer12;
public class Improve {
public static void main(String[] args) {
char[][] tests = {{'A','B','C','E'},{'S','F','C','S'},{'A','D','E','E'}};
String words = "SEE";
Improve improve = new Improve();
boolean res = improve.exist(tests, words);
System.out.println(res);
}
/**
* 思路分析:
* 总体思路:采用dfs or bfs 进行遍历
* 使用深度搜索,就是使用n便是word[]的第几位
* step1: 使用pathWord 存放符合条件数据,word[n]与遍历到的值进行比较,还需要一个char path[][]来表示以及走过的路径,初始全0,走过表示为1
* step2:dfs下一步应该怎么走,沿着四个方向走,定义四个方向,上[-1][] 下[+1][] 左[][-1] 右[][+1] 甚至可以将移动单独抽为一个函数,并且判断是否走过重复,和是否撞墙
*step3:结束当n == word.lenth时说明,以及遍历完了word返回true,当n周围都是已经遍历过得或者null时候就表示结束了
*
*
* 代码优化:
* path[y][x]置为0可以优化掉
*
* 四个方向可以优化掉
* 1. 通过使用directions[][] = {{-1,0},{0,1},{1,0},{0,-1}} 顺时针方向
* 2. x = x + direction[1],y = y+direction[0]
*
* @param board
* @param word
* @return
*/
char[] words ;
char[][] boards;
char[][] path;
int height;
int width;
int[][] directions = {{-1,0},{0,1},{1,0},{0,-1}};
int[] direction;
public boolean exist(char[][] board, String word) {
//定义路径变量
height = board.length;
width = board[0].length;
path = new char[height][width];
//定义n,x,y
int n = 0;
int x = 0;
int y = 0;
//将word和board的作用域扩大,方便调用
words = word.toCharArray();
boards = board;
//优化:遍历每一个值,都进行dfs,dfs中会进行判断
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if(dfs(n,j,i)){
return true;
}
}
}
return false;
}
/**
*
* @param n 表示遍历了几层,表示与words[n]相对应,当n == len时,我已经把words遍历完了
* @param x
* @param y
* @return
*/
public boolean dfs(int n,int x,int y){
//判断当前的值是否与words需要对比的值一样,一样则继续,不一样则停止dfs
if(!(boards[y][x] == words[n])){
return false;
}
//判断是否已经将words中的每一个值都进行了遍历,已经进行完了则返回true
if(n == words.length-1){
return true;
}
//将该点位确定为已经走过
path[y][x] = 1;
//四个方向,进行dfs
for (int i = 0; i < 4; i++) {
int[] direction = directions[i];
boolean canMove = canMove(x + direction[1], y + direction[0]);
if(canMove){
boolean resPart = dfs(n+1,x + direction[1],y + direction[0]);
if(resPart){
return resPart;
}
}
}
//只需要在dfs结束的时候将自己走过的放为0
path[y][x] = 0;
return false;
}
//判断能否这样移动,判断是否越界,判断是否已经在路径中
public boolean canMove(int x,int y){
//判断是否越界
if(x < 0){
return false;
}
if(x >= width){
return false;
}
if(y < 0){
return false;
}
if(y >= height){
return false;
}
//判断是否已经走过
if(path[y][x] == 1){
return false;
}
return true;
}
}