题目原文:
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.
题目大意:
给出一个由字母组成的二维矩阵,和一串单词,求哪些单词能用矩阵的一条路径表示。
题目分析:
本题是Middle-题目108:79. Word Search的延伸,如果简单的用一个for循环再调用第一题的函数,显然tle没商量。很容易想到是因为前缀相同的单词都进行了重复的搜索。那么针对相同前缀优化想到了什么?没错Trie树!
参考了discuss中一个最快的代码,大致意思是从矩阵的每一个点开始深搜,dfs的搜索路径和Trie树的搜索路径保持一致(即搜索到某一字符串时,当前dfs函数下的trie树节点也要对应一致),如果当前路径在trie树上是空节点,则直接退出;如果找到了一个Trie树节点它是单词,就加入答案并从Trie树上删掉这个单词。这样相同前缀的单词就只搜索了一次。
源码:(language:java)
public class Solution {
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;
}
}
成绩:
19ms,91.42%,21ms,4.36%