212.word-search-ii


212. 单词搜索 II

题目地址

给定一个二维网格 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(前缀树)

思路

  1. DFS words 遍历 ---> board search O(Nmm*4^k) k 为单词平均的长度

  2. trie

    a. all words ---> Trie 构建起 prefix b. board, BFS

解法一

dx = [-1, 1, 0, 0] 
dy = [0, 0, -1, 1] 
END_OF_WORD = "#"

class Solution:
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
        if not board or not board[0]: return [] 
        if not words: return []

        self.result = set()

        # 构建trie
        root = collections.defaultdict() 
        for word in words:
            node = root
            for char in word:
                node = node.setdefault(char, collections.defaultdict()) 
            node[END_OF_WORD] = END_OF_WORD

        self.m, self.n = len(board), len(board[0]) 
        for i in range(self.m):
            for j in range(self.n): 
                if board[i][j] in root:
                    self.dfs(board, i, j, "", root) 
        return list(self.result)

    def dfs(self, board, i, j, cur_word, cur_dict): 
        cur_word += board[i][j]
        cur_dict = cur_dict[board[i][j]]

        if END_OF_WORD in cur_dict:
            self.result.add(cur_word)

        tmp, board[i][j] = board[i][j], '@' 
        for k in range(4):
            x, y = i + dx[k], j + dy[k]
            if 0 <= x < self.m and 0 <= y < self.n \
            and board[x][y] != '@' and board[x][y] in cur_dict:
                self.dfs(board, x, y, cur_word, cur_dict) 
        board[i][j] = tmp
class Trie {

    class TrieNode {
        public char val;
        public boolean isWord;
        public TrieNode[] children = new TrieNode[26];
        public TrieNode(){}
        TrieNode(char c) {
            TrieNode node = new TrieNode();
            node.val = c;
        }
    }

    private TrieNode root;

    /** Initialize your data structure here. */
    public Trie() {
        root = new TrieNode();
        root.val = ' ';
    }

    /** Inserts a word into the trie. */
    public void insert(String word) {
        TrieNode ws = root;
        for(int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (ws.children[c - 'a'] == null) ws.children[c - 'a'] = new TrieNode(c);
            ws = ws.children[c - 'a'];
        }   
        ws.isWord = true;
    }

    /** Returns if the word is in the trie. */
    public boolean search(String word) {
        TrieNode ws = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (ws.children[c - 'a'] == null) return false;
            ws = ws.children[c - 'a'];
        }
        return ws.isWord;
    }

    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
        TrieNode ws = root;
        for(int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
           if (ws.children[c - 'a'] == null) return false; 
           ws = ws.children[c - 'a'];
        }
        return true;
    }
}

class Solution {

    Set<String> res = new HashSet<String>();

    public List<String> findWords(char[][] board, String[] words) {

        Trie trie = new Trie();
        for (String word: words) {
            trie.insert(word);
        }

        int m = board.length;
        int n = board[0].length;

        boolean[][] visited = new boolean[m][n];

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                dfs(board, visited, "", i, j, trie);
            }
        }
        return new ArrayList<String>(res);
    }

    private void dfs(char[][] board, boolean[][] visited, String str, int x, int y, Trie trie) {
        if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) return;
        if (visited[x][y]) return;

        str += board[x][y];
        if (!trie.startsWith(str)) return;

        if (trie.search(str)) res.add(str);

        visited[x][y] = true;
        dfs(board, visited, str, x - 1, y, trie);
        dfs(board, visited, str, x + 1, y, trie);
        dfs(board, visited, str, x, y - 1, trie);
        dfs(board, visited, str, x, y + 1, trie);
        visited[x][y] = false;
    }
}