212. 单词搜索 II

from typing import List, Dict, Optional, Set

class TrieNode:
    """Trie 树节点类"""
    def __init__(self):
        # 子节点字典:键是字符,值是对应的 TrieNode 子节点
        self.children: Dict[str, TrieNode] = {}
        # 存储到达此节点的完整单词(如果它是某个单词的结尾)
        # 初始化为 None,表示不是单词结尾
        self.word: Optional[str] = None 

class Solution:
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
        """
        在二维网格中查找单词列表中的所有单词。
        """
        if not board or not board[0] or not words:
            return []

        # 1. 构建 Trie 树
        root = TrieNode()
        for word in words:
            node = root
            for char in word:
                if char not in node.children:
                    node.children[char] = TrieNode()
                node = node.children[char]
            # 在单词结尾的节点存储单词本身
            node.word = word

        rows, cols = len(board), len(board[0])
        # 使用集合存储结果以自动去重
        result_set: Set[str] = set()
        # 方向数组,方便遍历邻居
        directions = [(0, 1), (0, -1), (1, 0), (-1, 0)] # 右, 左, 下, 上

        # 定义 DFS 辅助函数
        def dfs(r: int, c: int, current_node: TrieNode):
            # a. 越界或已访问检查
            if not (0 <= r < rows and 0 <= c < cols) or board[r][c] == '#':
                return

            # b. Trie 剪枝检查
            char = board[r][c]
            if char not in current_node.children:
                return
            
            # --- 状态更新与前进 ---
            # 移动到 Trie 的下一个节点
            next_node = current_node.children[char]
            
            # 临时标记当前单元格为已访问
            original_char = board[r][c]  # 保存原始字符
            board[r][c] = '#'   # 将当前单元格标记为“已在当前路径中访问过”

            # c. 检查是否找到单词
            if next_node.word is not None: #对于那些不是任何单词结尾的中间节点,它们的 word 属性保持初始值 None。
                # 找到了一个单词,添加到结果集
                result_set.add(next_node.word)
                # 将其从 Trie 中移除(或标记),避免重复查找和作为后续剪枝
                next_node.word = None 
                # 可选优化: 如果 next_node 也没有子节点了,可以考虑从父节点移除它来进一步剪枝
                # (这里为了简化,暂时不加这个优化)

            # d. 递归探索四个方向的邻居
            for dr, dc in directions:
                nr, nc = r + dr, c + dc
                dfs(nr, nc, next_node)

            # e. 回溯:恢复单元格状态
            board[r][c] = original_char #目的: 恢复状态,撤销在本层递归开始时所做的“标记访问”(board[r][c] = '#') 衔接: 它确保当 DFS 回溯到上一层(即调用 dfs(r, c, ...) 的那个地方)时,网格 board 的状态与进入 dfs(r, c, ...) 之前的状态是一致的(至少对于 (r, c) 这个单元格是这样) 如3中的 dfs(r, c, root)

        # 3. 遍历网格,从每个单元格启动 DFS
        for r in range(rows):
            for c in range(cols):
                # 从根节点开始 DFS
                dfs(r, c, root)

        # 4. 返回结果
        return list(result_set)

# 示例用法:
# solution = Solution()
# board1 = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]]
# words1 = ["oath","pea","eat","rain"]
# print(solution.findWords(board1, words1)) # 输出: ['eat', 'oath']

# board2 = [["a","b"],["c","d"]]
# words2 = ["abcb"]
# print(solution.findWords(board2, words2)) # 输出: []

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值