力扣79题:单词搜索

简介: 力扣79题:单词搜索

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。

会一些的技术:数据分析、算法、SQL、大数据相关、python

欢迎加入社区:码上找工作

作者专栏每日更新:

LeetCode解锁1000题: 打怪升级之旅

python数据分析可视化:企业实战案例

python源码解读

备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级

题目描述

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

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

输入格式
  • board:一个二维字符数组。
  • word:一个字符串,代表需要搜索的单词。
输出格式
  • 返回一个布尔值,表示单词是否在网格中。

示例

示例 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. 遍历网格:从每一个可能的起点开始,尝试所有路径直到找到匹配的单词。
  2. 递归搜索:从一个字符开始,递归地搜索四个方向(上、下、左、右)是否可以形成单词。
  3. 边界和条件:确保搜索不越界,且当前字符与单词字符匹配,未被访问过。
完整的规范代码
def exist(board, word):
    """
    使用回溯法搜索二维网格中的单词
    :param board: List[List[str]], 输入的二维字符网格
    :param word: str, 需要搜索的单词
    :return: bool, 单词是否存在于网格中
    """
    def backtrack(i, j, index):
        if index == len(word):
            return True
        if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]) or board[i][j] != word[index]:
            return False
        temp = board[i][j]
        board[i][j] = '#'  # 标记当前格子访问过
        found = (backtrack(i + 1, j, index + 1) or
                 backtrack(i - 1, j, index + 1) or
                 backtrack(i, j + 1, index + 1) or
                 backtrack(i, j - 1, index + 1))
        board[i][j] = temp  # 回溯
        return found
    for i in range(len(board)):
        for j in range(len(board[0])):
            if backtrack(i, j, 0):
                return True
    return False
# 示例调用
print(exist([['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E']], "ABCCED"))  # 输出: true
算法分析
  • 时间复杂度:(O(M * N * 4^L)),其中 (M) 和 (N) 是网格的维度,(L) 是单词的长度。每个字母有4个方向可以探索。
  • 空间复杂度:(O(L)),递归的深度最大为单词长度 (L)。

方法二:深度优先搜索(DFS)+ 剪枝

解题步骤
  1. 深度优先搜索:类似于方法一,但增加剪枝来避免不必要的搜索,例如提前检查单词中的字符在网格中的频率。
  2. 优化检索:在开始搜索前,比较网格中字符的可用性和单词的需求,如果不匹配则提前返回 false
完整的规范代码
def exist(board, word):
    from collections import Counter
    # 剪枝:先检查字符是否足够
    chars = Counter(ch for row in board for ch in row)
    if any(word.count(c) > chars[c] for c in word):
        return False
    def dfs(i, j, k):
        if not (0 <= i < len(board) and 0 <= j < len(board[0]) and board[i][j] == word[k]):
            return False
        if k == len(word) - 1:
            return True
        board[i][j], tmp = '', board[i][j]  # 避免访问自身
        found = (dfs(i + 1, j, k + 1) or
                 dfs(i - 1, j, k + 1) or
                 dfs(i, j + 1, k + 1) or
                 dfs(i, j - 1, k + 1))
        board[i][j] = tmp
        return found
    for i in range(len(board)):
        for j in range(len(board[0])):
            if dfs(i, j, 0):
                return True
    return False
# 示例调用
print(exist([['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E']], "ABCCED"))  # 输出: true
算法分析
  • 时间复杂度:优化前是 (O(M * N * 4^L)),剪枝后性能有所提高但最坏情况依然是 (O(M * N * 4^L))。
  • 空间复杂度:(O(L)),递归的深度最大为单词长度 (L)。

方法三:广度优先搜索(BFS)

解题步骤
  1. 广度优先搜索:使用队列实现层次遍历,每一层对应单词中的一个字符。
  2. 逐层搜索:每个位置尝试四个方向的扩展,寻找下一个字符。
完整的规范代码
def exist(board, word):
    from collections import deque
    def bfs(start):
        queue = deque([start])
        visited = set([start])
        index = 1
        while queue and index < len(word):
            next_queue = deque()
            while queue:
                i, j = queue.popleft()
                for ni, nj in [(i+1, j), (i-1, j), (i, j+1), (i, j-1)]:
                    if 0 <= ni < len(board) and 0 <= nj < len(board[0]) and (ni, nj) not in visited and board[ni][nj] == word[index]:
                        next_queue.append((ni, nj))
                        visited.add((ni, nj))
            queue = next_queue
            index += 1
        return index == len(word)
    for i in range(len(board)):
        for j in range(len(board[0])):
            if board[i][j] == word[0] and bfs((i, j)):
                return True
    return False
# 示例调用
print(exist([['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E']], "ABCCED"))  # 输出: true
算法分析
  • 时间复杂度:(O(M \times N * 4^L)),尽管使用了广度优先搜索,但复杂度相当于深度优先搜索。
  • 空间复杂度:(O(min(L, M * N))),队列的最大长度取决于网格大小和单词长度的较小值。

方法四:动态规划

解题步骤
  1. 动态规划表达:尝试将搜索转化为动态规划问题,使用 (dp[i][j][k]) 表示从 (i, j) 开始的后缀是否可以形成 (word[k:])。
  2. 状态转移:根据四个方向的可能性更新状态。
完整的规范代码
def exist(board, word):
    m, n, l = len(board), len(board[0]), len(word)
    dp = [[[-1] * l for _ in range(n)] for _ in range(m)]
    def search(x, y, k):
        if k == l:
            return True
        if not (0 <= x < m and 0 <= y < n and board[x][y] == word[k]):
            return False
        if dp[x][y][k] != -1:
            return dp[x][y][k]
        board[x][y], temp = '#', board[x][y]  # 避免重复使用
        result = (search(x + 1, y, k + 1) or
                  search(x - 1, y, k + 1) or
                  search(x, y + 1, k + 1) or
                  search(x, y - 1, k + 1))
        dp[x][y][k] = result
        board[x][y] = temp
        return result
    for i in range(m):
        for j in range(n):
            if search(i, j, 0):
                return True
    return False
# 示例调用
print(exist([['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E']], "ABCCED"))  # 输出: true
算法分析
  • 时间复杂度:(O(M * N * L)),每个位置对应单词长度的状态需要计算。
  • 空间复杂度:(O(M * N * L)),存储每个位置对应单词的所有状态。

方法五:库函数利用

这题目不适合直接使用库函数解决,因为它涉及到具体的路径搜索问题,不是简单的计算或数据操作。故省略此方法。

不同算法的优劣势对比

特征 方法一:回溯法 方法二:DFS+剪枝 方法三:BFS 方法四:动态规划 方法五:库函数
时间复杂度 (O(M * N * 4^L)) (O(M * N * 4^L)) (O(M * N * 4^L)) (O(M * N * L)) N/A
空间复杂度 (O(L)) (O(L)) (O(min(L, M * N))) (O(M * N * L)) N/A
优势 灵活,直观 剪枝提高效率 层次搜索明确 解决重复计算问题 不适用
劣势 可能过慢 实现复杂 空间利用较大 空间消耗大 不适用

应用示例

游戏开发:在迷宫或解谜游戏中找到特定路径或解决方案。

图像处理:在图像识别中寻找特定模式的路径。

机器学习:在数据预处理中,从复杂的数据结构中提取特征。


欢迎关注微信公众号 数据分析螺丝钉

相关文章
|
12天前
|
算法
【LeetCode刷题】滑动窗口解决问题:串联所有单词的子串(困难)、最小覆盖子串(困难)
【LeetCode刷题】滑动窗口解决问题:串联所有单词的子串(困难)、最小覆盖子串(困难)
|
16天前
|
存储 SQL 算法
LeetCode题58: 5种算法实现最后一个单词的长度【python】
LeetCode题58: 5种算法实现最后一个单词的长度【python】
|
28天前
|
存储 C语言 容器
从C语言到C++_26(set+map+multiset+multimap)力扣692+349+牛客_单词识别(下)
从C语言到C++_26(set+map+multiset+multimap)力扣692+349+牛客_单词识别
25 1
|
28天前
|
存储 C语言 容器
从C语言到C++_26(set+map+multiset+multimap)力扣692+349+牛客_单词识别(中)
从C语言到C++_26(set+map+multiset+multimap)力扣692+349+牛客_单词识别
27 1
|
28天前
|
存储 自然语言处理 C语言
从C语言到C++_26(set+map+multiset+multimap)力扣692+349+牛客_单词识别(上)
从C语言到C++_26(set+map+multiset+multimap)力扣692+349+牛客_单词识别
31 1
|
12天前
|
算法
【经典LeetCode算法题目专栏分类】【第6期】二分查找系列:x的平方根、有效完全平方数、搜索二位矩阵、寻找旋转排序数组最小值
【经典LeetCode算法题目专栏分类】【第6期】二分查找系列:x的平方根、有效完全平方数、搜索二位矩阵、寻找旋转排序数组最小值
|
12天前
|
算法
【经典LeetCode算法题目专栏分类】【第3期】回溯问题系列:单词搜索、N皇后问题、判断有效数独、解数独
【经典LeetCode算法题目专栏分类】【第3期】回溯问题系列:单词搜索、N皇后问题、判断有效数独、解数独
|
15天前
|
SQL 算法 数据可视化
LeetCode题目99:图解中叙遍历、Morris遍历实现恢复二叉树搜索树【python】
LeetCode题目99:图解中叙遍历、Morris遍历实现恢复二叉树搜索树【python】
|
15天前
|
存储 算法 数据可视化
python多种算法对比图解实现 验证二叉树搜索树【力扣98】
python多种算法对比图解实现 验证二叉树搜索树【力扣98】
|
15天前
|
存储 算法 数据挖掘
LeetCode 题目 81:搜索旋转排序数组 II
LeetCode 题目 81:搜索旋转排序数组 II