Leetcode 79:单词搜索(最详细的解法!!!)

给定一个二维网格和一个单词,找出该单词是否存在于网格中。

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

示例:

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

给定 word = "ABCCED", 返回 true.
给定 word = "SEE", 返回 true.
给定 word = "ABCB", 返回 false.

解题思路

这实际上还是一个回溯法解决的问题。例如,对于word = 'ABCCED',我们从第一个元素开始,首先匹配到A,然后向后面寻找。我们规定好寻找的顺序为:⬆️,➡️,⬇️,⬅️。我们接着找B,上面越界,右边找到了。我们接着找C,上面越界,右边找到了。我们接着找C,上面越界了,右边不对,下面找到了。接着找E,我们发现上面访问过,不再访问。接着向右查找,发现不匹配,接着向下查找,发现越界了,接着想做查找,OK!我们所有元素匹配成功。

class Solution:
    def _searchWord(self, board, word, index, x, y, visited):
        def inArea(x, y):
            return 0 <= x < self.lx and 0 <= y < self.ly

        d = [(-1, 0), (0, 1), (1, 0), (0, -1)]
        if index + 1 == len(word):
            return board[x][y] == word[index]

        if board[x][y] == word[index]:
            visited[x][y] = True
            for i in range(4):
                x_ = x + d[i][0]
                y_ = y + d[i][1]
                if inArea(x_, y_) and not visited[x_][y_] and \
                self._searchWord(board, word, index + 1, x_, y_, visited):
                    return True
            visited[x][y] = False      
        return False

    def exist(self, board, word):
        """
        :type board: List[List[str]]
        :type word: str
        :rtype: bool
        """
        if not word or not board:
            return False
        
        self.lx, self.ly = len(board), len(board[0])
        visited = [[False]*self.ly for _ in range(self.lx)]

        for i, val in enumerate(board):
            for j, _ in enumerate(val):
                if self._searchWord(board, word, 0, i, j, visited):
                    return True

        return False

我们对这样的问题有这样的一步优化处理,我们先统计wordboard的字符以及各字符的个数,比较这两者是不是一个包含的关系,也就是word <= board,如果不是,我们直接返回false。这种优化,对于返回bool类型的问题有一定的作用。

我们下面的写法没有使用visited,而是使用了临时的tmp,存储board[i][j]是否访问,如果访问过了,我们将board[i][j]='#'

import collections
class Solution:
    def _exist(self, board, i, j, word, k):
        if k == len(word):
            return True
        
        if i < 0 or i >= len(board) or j < 0 or j >= len(board[i]):
            return False
        
        if board[i][j] == word[k]:
            temp = board[i][j]
            board[i][j] = '#'
            for d in range(4):
                if self._exist(board, i + self.direct[d][0], j + self.direct[d][1], word, k + 1):
                    return True
            board[i][j] = temp
        return False
   
    def exist(self, board, word):
        """
        :type board: List[List[str]]
        :type word: str
        :rtype: bool
        """
        if not board:
            return False
        
        board_c = collections.Counter([c for row in board for c in row])
        word_c = collections.Counter(word)
        for c in word_c:
            if not c in word_c or word_c[c] > board_c[c]:
                return False
            
        r, c = len(board), len(board[0])
        self.direct = [(-1, 0), (0, 1), (1, 0), (0, -1)]
        
        for i in range(r):
            for j in range(c):  
                if self._exist(board, i, j, word, 0):
                    return True
        return False

注意上面两个代码边界条件写法上的区别,第一个代码是if index + 1 == len(word):和第二个代码不同(第二个代码写法更好)。第一个代码不能采用第二个代码的写法,原因在于避免board:[["a"]] word:"a"这个问题。

同样的,对于递归解决的问题,我们都应该思考一下怎么通过迭代去解决。

class Solution:  
    def exist(self, board, word):
        """
        :type board: List[List[str]]
        :type word: str
        :rtype: bool
        """
        q = list()

        lx, ly = len(board), len(board[0]) 
        for r in range(lx): 
            for c in range(ly):
                if board[r][c] == word[0]:
                    q.append((r, c))

        def neighbors(board, r, c):
            directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]
            nbs = []
            for d in directions:
                nr = r + d[0]
                nc = c + d[1]
                if 0 <= nr < lx and 0 <= nc < ly:
                    nbs.append((nr, nc))
            return nbs

        for (r, c) in q:
            visited = set()
            stack = list()
            stack.append((r, c, 0, False)) 
            while stack:
                cr, cc, i, bt = stack.pop()
                if bt:
                    visited.remove((cr, cc))
                    continue
                    
                visited.add((cr, cc))
                stack.append((cr, cc, i, True))
                if i == (len(word) - 1):
                    return True
            
                for nr, nc in neighbors(board, cr, cc):
                    if (nr, nc) in visited:
                        continue
                    if board[nr][nc] == word[i + 1]:
                        stack.append((nr, nc, i + 1, False))
            
        return False

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值