python每日一题【剑指 Offer 12. 矩阵中的路径】

day22-2022.11.17

题目信息来源
作者:Krahets
链接:https://leetcode.cn/leetbook/read/illustration-of-algorithm
来源:力扣(LeetCode)

剑指 Offer 12. 矩阵中的路径

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

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

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false

提示:

  • m == board.length
  • n = board[i].length
  • 1 <= m, n <= 6
  • 1 <= word.length <= 15
  • board 和 word 仅由大小写英文字母组成

知识点

  1. 深度优先搜索DFS

链接:https://leetcode.cn/leetbook/read/illustration-of-algorithm/58d5vh/

可以理解为暴力法遍历矩阵中所有字符串可能性。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。

  • 递归参数
  • 终止条件
  • 递推工作
  • 返回值
  1. 剪枝:将要遍历的先进行判断,不可能的直接去除

题解

我的垃圾版本,不会回溯,暂时没想到好的办法,出现错误的测试为 [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]],"SEE",这个刚好就是一个需要回溯的例子。

class Solution:
    def exist(self, board, word) -> bool:
        # 需要一个列表存储,存储已选择的路径
        # 有可能存在多个选择,存在一个候选列表,如果下一个也满足,再加入已选列表
        # 坐标的变化,要考虑边界
        # 如果找不到,就为False
        choose_list = []        # 已选点集合
        candidate_list = []     # 候选集合
        # 找起点
        result = False
        x, y, k = 0, 0, 0
        for i in range(len(board)):
            for j in range(len(board[0])):
                # 找起点
                if board[i][j]==word[0]:
                    x, y = i, j # 初始化起点,从第二个字母开始找
                    choose_list=[[x, y]]
                    for k in range(1, len(word)):
                        find_letter = False
                        # letter候选值的坐标
                        if x-1>=0 and [x-1,y] not in choose_list:candidate_list.append([x-1,y])
                        if x+1<len(board) and [x+1,y] not in choose_list:candidate_list.append([x+1,y])
                        if y-1>=0 and [x,y-1] not in choose_list:candidate_list.append([x,y-1])
                        if y+1<len(board[0]) and [x,y+1] not in choose_list:candidate_list.append([x,y+1])
                        # 遍历候选坐标
                        for m,n in candidate_list:
                            # 如果当前m,n符合要求,则将m,n纳入choose_list,更新x,y为m,n
                            if board[m][n]==word[k]:
                                choose_list.append([m,n])
                                x, y = m, n
                                find_letter = True
                                # 如果找到了,就不用再找了,继续下一个字母
                                break
                        candidate_list = []
                        result = find_letter
                        if find_letter==False:
                            break
                        elif find_letter==True and k==len(word)-1:
                            return find_letter
        return result

题解:官方

前一章是动态规划,就想着是不是不用回溯好。但是这个题,似乎还是递归好一点:因为子问题结构相似。我的破方法就不能够回溯,在 (i-1,j), (i+1,j), (i,j-1), (i,j+1) 四种并列可能性中有一个即可。有一条路可以走通即可,但是我这个如果一个路一开始走通,后面走不通,就不能回去寻找未尝试的并列可能性。

链接:https://leetcode.cn/leetbook/read/illustration-of-algorithm/58d5vh/

DFS 解析:

  • 递归参数: 当前元素在矩阵 board 中的行列索引 i 和 j ,当前目标字符在 word 中的索引 k 。
  • 终止条件:
    • 返回 false : (1) 行或列索引越界 或 (2) 当前矩阵元素与目标字符不同 或 (3) 当前矩阵元素已访问过 ( (3) 可合并至 (2) ) 。
    • 返回 true: k = len(word) - 1 ,即字符串 word 已全部匹配。
  • 递推工作:
    • 标记当前矩阵元素: 将 b o a r d [ i ] [ j ] board[i][j] board[i][j]修改为 空字符 ‘’ ,代表此元素已访问过,防止之后搜索时重复访问。
    • 搜索下一单元格: 朝当前元素的 上、下、左、右 四个方向开启下层递归,使用 或 连接 (代表只需找到一条可行路径就直接返回,不再做后续 DFS ),并记录结果至 res 。
    • 还原当前矩阵元素: 将 b o a r d [ i ] [ j ] board[i][j] board[i][j]元素还原至初始值,即 $word[k] $。
  • 返回值: 返回布尔量 res ,代表是否搜索到目标字符串。
class Solution:
    def exist(self, board, word) -> bool:
        def find(i, j, k):
            if i<0 or i>=len(board) or j<0 or j>=len(board[0]) or board[i][j]!=word[k]:return False
            if k==len(word)-1:return True
            board[i][j] = ''
            result = find(i-1,j,k+1) or find(i+1,j,k+1) or find(i,j-1,k+1) or find(i,j+1,k+1)
            board[i][j] = word[k]
            return result
        k = 0
        for i in range(len(board)):
            for j in range(len(board[0])):
                if find(i,j,k):return True
        return False
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

piukaty

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值