汇总:剑指offer算法合集
题目
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
例如,在下面的 3×4 的矩阵中包含单词 “ABCCED”(单词中的字母已标出)。
示例 1:
输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
示例 2:
输入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
输出:false
提示:
1 <= board.length <= 200
1 <= board[i].length <= 200
board 和 word 仅由大小写英文字母组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof
解题思路
- 由于二维数组每个位置都可能是目标字符串第0个字符对应的起点,因此只能遍历二维数组,把每个位置作为起点进行查找目标字符串
- 确定起点后,下一步可走的方向有四个,之后每一步可走方向只有三个,因为有一个方向是上次走来的方向,但我们不知道上一次走来的方向,因此依然会在每个位置的四个方向都尝试
- 走过的位置用一个非字母的字符占位,表示当前位置不可重复走过。但如果尝试后面的方向突然走不通了,需要回溯,回溯后需要重新把原来的字母放回原位置上
- 每次只尝试一个方向,直到走不通就回溯,再尝试走别的方向,全部走过的位置的所有方向都试过了都走不通,说明当前起点是错误的,因此需要寻找下一个起点,然后重复前面寻找下一步的步骤。如果在走的过程中发现走过的长度已经等于目标字符串的长度,则直接返回true,说明找到了目标字符串
复杂度分析
二维数组遍历,时间复杂度为O(mn)
,而每个起点继续深度搜索,其中有一个方向已走过,必定走不通,因此只有三个方向是真正需要走的,而每个方向可能需要走差不多等于目标字符串长度的路,假设目标字符串长度为k,则这里的时间复杂度为O(3k),总的时间复杂度为O(3kmn)。而空间方面主要是递归k的深度,所以正常情况下是O(k)
,在极端的情况下需要递归mn的深度,此时空间复杂度为O(mn)
代码实现
class Solution {
public boolean exist(char[][] board, String word) {
//把字符串先转char数组,可以加快访问
char[] cword = word.toCharArray();
//遍历二维数组找起点
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
//检查每个起点是否能找到目标字符串
if (check(i, j, 0, board, cword)) {
return true;
}
}
}
return false;
}
boolean check(int i, int j, int k, char[][] board, char[] word) {
//到达边界或者当前位置的字母和目标字符串的字母不对应,则中断查找
if (i >= board.length || i < 0 || j >= board[i].length
|| j < 0 || board[i][j] != word[k]) return false;
//如果当前找到的路径和目标字符串一致了,说明找到了
if (k == word.length - 1) return true;
int kk = k + 1;
//把走过的位置置为空,代表已经走过了
board[i][j] = ' ';
boolean isExist = check(i + 1, j, kk, board, word) || check(i - 1, j, kk, board, word)
|| check(i, j + 1, kk, board, word) || check(i, j - 1, kk, board, word);
//回溯回来后,把之前置为空的位置还原
//能走到这里说明原来位置的字母等于目标字符串第k个字母
//所以直接把目标字符串第k个字母放回去即可
board[i][j] = word[k];
return isExist;
}