leetcode刷题日记-【79. 单词搜索】

132 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第31天,点击查看活动详情

题目描述

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

 

示例 1:

image.png

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED" 输出:true 示例 2:

image.png

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE" 输出:true 示例 3:

image.png

输入: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数组的大小