算法:212. Word Search II单词搜索II

212. Word Search II

Given an m x n board of characters and a list of strings words, return all words on the board.

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

Example 1:
算法:212. Word Search II单词搜索II_第1张图片

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

Example 2:
算法:212. Word Search II单词搜索II_第2张图片

Input: board = [["a","b"],["c","d"]], words = ["abcb"]
Output: []

Constraints:

m == board.length
n == board[i].length
1 <= m, n <= 12
board[i][j] is a lowercase English letter.
1 <= words.length <= 3 * 10^4
1 <= words[i].length <= 10
words[i] consists of lowercase English letters.
All the strings of words are unique.

分析

直观地,从每个单元格开始,然后尝试在字典中建立一个单词。Backtracking (dfs)是用尽所有可能方法的有力方法。显然,pruning当当前字符不在任何单词中时,我们需要执行此操作。

  1. 我们如何立即知道当前字符无效?HashMap?
  2. 我们如何立即知道下一个有效字符是什么?LinkedList?
  3. 但是可以从字符列表中选择下一个字符。“Mutil-LinkedList”?

梳理它们Trie是自然的选择。注意:

  1. TrieNode这就是我们所需要的。search而且startsWith没有用。
  2. 无需在TrieNode上存储字符。c.next[i] != null足够的。
  3. 永远不要使用c1 + c2 + c3。使用StringBuilder。
  4. 无需使用O(n^2)多余的空间visited[m][n].
  5. 无需使用StringBuilder。将word自身存储在叶节点就足够了。
  6. 无需使用HashSet去重复。使用“一次搜索”特里。

有关更多说明,请访问Dietpepsi的博客。

代码优化
更新:感谢@dietpepsi,我们从进一步改进17ms了15ms。

  1. 59ms:使用search和startsWith在Trie类中使用这种流行的解决方案。
  2. 33ms:删除root在每次dfs调用中都不必要的Trie类。
  3. 30ms:使用w.toCharArray()代替w.charAt(i)。
  4. 22ms:使用StringBuilder代替c1 + c2 + c3。
  5. 20ms:StringBuilder通过存储word而不是boolean在TrieNode中完全删除。
  6. 20ms:visited[m][n]通过board[i][j] = '#'直接修改将其完全删除。
  7. 18ms:检查有效性,例如,if(i > 0) dfs(…)在转到下一个之前dfs。
  8. 17ms:c - a使用一个变量进行重复数据删除i。
  9. 15ms:HashSet完全移除。Dietpepsi的想法很棒。

最终运行时间为15ms。希望能帮助到你!

代码实现

public List<String> findWords(char[][] board, String[] words) {
     
    List<String> res = new ArrayList<>();
    TrieNode root = buildTrie(words);
    for (int i = 0; i < board.length; i++) {
     
        for (int j = 0; j < board[0].length; j++) {
     
            dfs (board, i, j, root, res);
        }
    }
    return res;
}

public void dfs(char[][] board, int i, int j, TrieNode p, List<String> res) {
     
    char c = board[i][j];
    if (c == '#' || p.next[c - 'a'] == null) return;
    p = p.next[c - 'a'];
    if (p.word != null) {
        // found one
        res.add(p.word);
        p.word = null;     // de-duplicate
    }

    board[i][j] = '#';
    if (i > 0) dfs(board, i - 1, j ,p, res); 
    if (j > 0) dfs(board, i, j - 1, p, res);
    if (i < board.length - 1) dfs(board, i + 1, j, p, res); 
    if (j < board[0].length - 1) dfs(board, i, j + 1, p, res); 
    board[i][j] = c;
}

public TrieNode buildTrie(String[] words) {
     
    TrieNode root = new TrieNode();
    for (String w : words) {
     
        TrieNode p = root;
        for (char c : w.toCharArray()) {
     
            int i = c - 'a';
            if (p.next[i] == null) p.next[i] = new TrieNode();
            p = p.next[i];
       }
       p.word = w;
    }
    return root;
}

class TrieNode {
     
    TrieNode[] next = new TrieNode[26];
    String word;
}

参考

https://leetcode.com/problems/word-search-ii/discuss/59780/Java-15ms-Easiest-Solution-(100.00)

你可能感兴趣的:(算法)