单词搜索 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(前缀树)。
分析
核心思想:DFS + 前缀树
关于提示中提到的 实现Trie(前缀树) 一题,解法见 LeetCode 高级 - 实现 Trie(前缀树)
本题的提示很明确,就是在引导我们使用前缀树匹配,减少回溯规模,以通过大数据量的测试。(貌似不采取前缀树,直接常规 DFS 会 TLE)
至于 DFS,套路很清晰,见代码即可。
代码
class Solution {
private TrieNode root = new TrieNode();
private int[] row = new int[]{-1,1,0,0};
private int[] col = new int[]{0,0,-1,1};
public List<String> findWords(char[][] board, String[] words) {
//直接用 list 存,会出现 重复记录word 的情况。。。汗颜!
//List<String> results = new ArrayList<String>();
//改用 HashSet 暂存结果,确保不会出现重复word
HashSet<String> temp = new HashSet<>();
//前缀树
for(int i=0;i<words.length;i++){
TrieNode node = root;
for(int j=0;j<words[i].length();j++)
node = node.add(words[i].charAt(j));
node.word = words[i];
}
//DFS
boolean[][] visited = new boolean[board.length][board[0].length];
for(int i=0;i<board.length;i++){
for(int j=0;j<board[0].length;j++){
if(root.ifExists(board[i][j])){
find(board,visited,i,j,root,temp);
}
}
}
List<String> results = new ArrayList<String>();
results.addAll(temp);
return results;
}
private void find(char[][] board,boolean[][] visited,int x,int y,TrieNode node,HashSet<String> temp){
visited[x][y]=true;
TrieNode cur = node.next[board[x][y]-'a'];
//到达可匹配子串,记录当前 word
if(cur.word!=null)
temp.add(cur.word);
for(int i=0;i<4;i++){
int x2 = x + row[i];
int y2 = y + col[i];
if(x2>=0 && x2<board.length && y2 >=0 && y2 < board[0].length && !visited[x2][y2] ){
if(cur.ifExists(board[x2][y2])){
find(board,visited,x2,y2,cur,temp);
}
}
}
visited[x][y] = false;
}
//前缀树
class TrieNode{
String word;
TrieNode[] next = new TrieNode[26];
TrieNode add(char chr){
if(next[chr-'a'] != null)
return next[chr-'a'];
next[chr-'a'] = new TrieNode();
return next[chr-'a'];
}
boolean ifExists(char chr){
return (next[chr - 'a']==null)?false:true;
}
}
}