携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第31天,点击查看活动详情
题目描述
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例 1:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED" 输出:true 示例 2:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE" 输出:true 示例 3:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB" 输出:false
提示:
- m == board.length
- n = board[i].length
- 1 <= m, n <= 6
- 1 <= word.length <= 15
- board 和 word 仅由大小写英文字母组成
进阶:你可以使用搜索剪枝的技术来优化解决方案,使其在 board 更大的情况下可以更快解决问题?
解题思路
给定一个二维数组board,一个字符串word,判断board中是否存在连续的字母使它组合成单词word。
首先判断题型,需要从一个二维数组中判断是否存在某种排列,涉及到排列的问题,所以可以采用回溯的方法。
确定本题回溯的三要素
路径
当前选择方向已经做出的选择,这里可以采用索引计数的方式来记录已经匹配到字符串word的哪个位置;
可做出的选择
在矩阵中,每次可做出的选择除了第一次以外有4个方向,下一次均是3个方向,为了减少变量量(需要记录已经做了哪些选择),此时就可以用一个二维数组来专门记录已经做出过的选择,下一次路径就直接取没有选择过的路径;
终止条件
终止条件为路径所在的索引等于word的长度,且仍旧是匹配的,则返回true;或者可供选择的路径为0直接返回false。
代码实现
public static boolean exist(char[][] board, String word) {
// 创建一个二维数组用来记录已经做出的选择
// 默认值为false,true代表已经做出过同样的选择,直接忽略即可
boolean[][] choosePath = new boolean[board.length][board[0].length];
for (int i=0 ; i < board.length ; i++ ) {
for (int j=0 ; j < board[0].length ; j++) {
// 可做出的选择
if (word.charAt(0) != board[i][j]) {
continue;
}
choosePath[i][j] = true;
boolean flag = backTrack(board, word, choosePath, 1, i, j);
if (flag) {
return true;
}
// 回溯
choosePath[i][j] = false;
}
}
return false;
}
private static boolean backTrack(char[][] board, String word, boolean[][] choosePath, int pathIndex , int i , int j) {
if (pathIndex == word.length()) {
return true;
}
// 四个方向可以选择
int[][] direct = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
for (int[] ints : direct) {
int newI = i + ints[0];
int newJ = j + ints[1];
if (newI >= 0 && newI < board.length && newJ >= 0 && newJ < board[0].length && !choosePath[newI][newJ] && board[newI][newJ] == word.charAt(pathIndex)) {
choosePath[newI][newJ] = true;
boolean flag = backTrack(board, word, choosePath,pathIndex+1, newI, newJ);
if (flag) {
return true;
}
choosePath[newI][newJ] = false;
}
}
return false;
}
复杂度
时间复杂度
O(mn*3^l),其中m、n分别是矩阵的长宽,l是给定word的长度。两层循环,第一层循环是矩阵的行,第二层是矩阵的列,循环里面还有一个递归,这里因为除了第一次可以做出4次选择外,其余的能做的选择只有3次(这里用了个二维数组记录已经选择过的方向,所以之后的选择方向实际上只有三次)。
空间复杂度
O(mn),用于记录已选择过的路径。即choosePath数组的大小