212. Word Search II

Given a 2D board and a list of words from the dictionary, find all words in the board.

Each word must be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.

For example,
Given words = ["oath","pea","eat","rain"] and board =

[
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]
Return ["eat","oath"].
Note:
You may assume that all inputs are consist of lowercase letters a-z.

click to show hint.

You would need to optimize your backtracking to pass the larger test. Could you stop backtracking earlier?

If the current candidate does not exist in all words' prefix, you could stop backtracking immediately. What kind of data structure could answer such query efficiently? Does a hash table work? Why or why not? How about a Trie? If you would like to learn how to implement a basic trie, please work on this problem: Implement Trie (Prefix Tree) first.

0. 

Trie数据结构,也就是前缀树。然后dfs时,如果当前形成的单词不在Trie里,就没必要继续dfs下去了。如果当前字符串在trie里,就说明board可以形成这个word。

这道题很好的体现了Trie的优势:不用Trie, 我们就得把String[] words里面的word一个一个去board里做dfs(以每个点为起点做一次),很耗时。有了Trie,我们可以用Trie存着所有word,然后去board里做一次dfs(以每个点为起点做一次)

1. return 千万不能要,否则会出如下的错

Input:

["ab","aa"]
["aba","baa","bab","aaab","aaa","aaaa","aaba"]

Expected answer
["aaa","aba","aaba","baa","aaab"]
Your answer
["aaa","aba","baa","aaba"]

aaa被添加之后,return了,导致另一个正确答案aaab不被添加,这跟以前的情况不同,以前都是答案等长,这里不一定

我觉得这里的复杂度是O(m*n*4^k),因为对于每个cell来说,有4个方向需要探索

2. 

有可能board里面有多份某一个word,dfs会把它们都找出来,但是result set只需要添加一次就好了

有人是这样做的:Set<String> res = new HashSet<String>();

return的时候: return new ArrayList<String>(res);

或者去除掉trie中的word

public class Solution {
    private class TrieNode {
    TrieNode[] next = new TrieNode[26];
    String word;
}
    private TrieNode buildTrie(String[] words) {
        TrieNode root = new TrieNode();
        for (String word : words) {
            TrieNode cur = root;
            for (char c : word.toCharArray()) {
                
                int i = c - 'a';
                if (cur.next[i] == null) {
                    cur.next[i] = new TrieNode();
                }
                cur = cur.next[i];
            }
            cur.word = word;
        }
        return root;
    }
   public List<String> findWords(char[][] board, String[] words) {
        List<String> res = new ArrayList<String>();
        if (board==null || board.length==0 || words==null || words.length==0) return res;
        TrieNode root = buildTrie(words);
        
        for (int i=0; i<board.length; i++) {
            for (int j=0; j<board[0].length; j++) {
                dfs(res, i, j, board, root);
            }
        }
        return res;
    }
    
    public void dfs(List<String> res, int i, int j, char[][] board, TrieNode root) {
        if (i<0 || j<0 || i>=board.length || j>=board[0].length || 
            board[i][j] == '#' || root.next[board[i][j] - 'a'] == null) return;
        char c = board[i][j];
        board[i][j] = '#';
        root = root.next[c - 'a'];
        if (root.word != null) {
           
            res.add(root.word);
            root.word = null;
        }
        
       
       
        dfs(res, i-1, j, board,  root);
        dfs(res, i+1, j, board,  root);
        dfs(res, i, j-1, board,  root);
        dfs(res, i, j+1, board,  root);
        //cur = cur.substring(0, cur.length()-1);
        board[i][j] = c;
    }
}

Code Optimization


UPDATE: Thanks to @dietpepsi we further improved from 17ms to 15ms.

  1. 59ms: Use search and startsWith in Trie class like this popular solution.
  2. 33ms: Remove Trie class which unnecessarily starts from root in every dfs call.
  3. 30ms: Use w.toCharArray() instead of w.charAt(i).
  4. 22ms: Use StringBuilder instead of c1 + c2 + c3.
  5. 20ms: Remove StringBuilder completely by storing word instead of boolean in TrieNode.
  6. 20ms: Remove visited[m][n] completely by modifying board[i][j] = '#' directly.
  7. 18ms: check validity, e.g., if(i > 0) dfs(...), before going to the next dfs.
  8. 17ms: De-duplicate c - a with one variable i.
  9. 15ms: Remove HashSet completely. dietpepsi's idea is awesome.

  

原文地址:https://www.cnblogs.com/apanda009/p/7249545.html