Leetcode 212. 单词搜索 II

186 阅读2分钟

给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。

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

示例:

输入: words = ["oath","pea","eat","rain"] and board = [ ['o','a','a','n'], ['e','t','a','e'], ['i','h','k','r'], ['i','f','l','v'] ]

输出: ["eat","oath"] 说明: 你可以假设所有输入都由小写字母 a-z 组成。

提示:

你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯? 如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这个问题: 实现Trie(前缀树)。

const dirs = [
  [-1, 0],
  [0, 1],
  [1, 0],
  [0, -1],
];

// 构建一个trie树,从而查找,思路很简单
// Y 最长的字符串长度
// X words的长度
// 字符串操作使用,trie树,算法时间复杂度O(4 * min(Y,mn)* mn)
// 空间复杂度 O(26Y)
export default (board, words) => {
  let res = [];
  const m = board.length;
  const n = board[0].length;

  const buildTrie = () => {
    const root = {};
    for (const w of words) {
      let node = root;
      for (const c of w) {
        if (node[c] == null) {
          node[c] = {};
        }
        node = node[c];
      }
      node.word = w;
    }
    return root;
  };

  const search = (node, x, y) => {
    if (node.word != null) {
      res.push(node.word);
      node.word = null;
    }
    if (x < 0 || x >= m || y < 0 || y >= n) return;

    const c = board[x][y];
    if (c === "#" || node[board[x][y]] == null) return;

    board[x][y] = "#"; // Mark visited
    // 四个方向查找
    for (const [dx, dy] of dirs) {
      const i = x + dx;
      const j = y + dy;
      search(node[c], i, j);
    }
    board[x][y] = c; // Reset
  };

  const root = buildTrie();
  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      search(root, i, j);
    }
  }
  return res;
};