题目链接: https://leetcode.com/problems/word-search-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(前缀树)。
思路一:
普通回溯法,从字典中依次取出每一个单词,然后碰到board中有首字母相同的就记入回溯。
缺点是回溯速度较慢。
AC 417ms
class Solution {
Set<String> set=new HashSet();
public List<String> findWords(char[][] board, String[] words) {
if(board==null||board.length==0||board[0].length==0)
return new ArrayList<String>();
for(String str:words){
char c=str.charAt(0);
char[] cur=str.toCharArray();
for(int i=0;i<board.length;i++){
for(int j=0;j<board[0].length;j++){
if(board[i][j]==c){
boolean[][] isVisited=new boolean[board.length][board[0].length];
backtrace(board,cur,0,i,j,isVisited);
}
}
}
}
return new ArrayList(set);
}
public void backtrace(char[][] board,char[] cur,int index,int i,int j,boolean[][] isVisited){
if(i<0||j<0||i>=board.length||j>=board[0].length
||index>=cur.length||isVisited[i][j]==true||board[i][j]!=cur[index])
return;
isVisited[i][j]=true;
if(index==cur.length-1)
set.add(String.valueOf(cur));
backtrace(board,cur,index+1,i,j+1,isVisited);
backtrace(board,cur,index+1,i+1,j,isVisited);
backtrace(board,cur,index+1,i,j-1,isVisited);
backtrace(board,cur,index+1,i-1,j,isVisited);
isVisited[i][j]=false;
}
}
思路二:
利用trie树来构建,这样可以优化回溯的重复情形。
AC 9ms:
class TrieNode{
TrieNode[] children=new TrieNode[26];
String word;
}
class Solution {
List<String> ans=new ArrayList();
public List<String> findWords(char[][] board, String[] words) {
if(board==null||board.length==0||board[0].length==0)
return ans;
TrieNode root=buildTrie(words);
for(int i=0;i<board.length;i++){
for(int j=0;j<board[0].length;j++){
boolean[][] isVisited=new boolean[board.length][board[0].length];
backtrack(board,i,j,root,isVisited);
}
}
return ans;
}
public void backtrack(char[][] board,int i,int j,TrieNode temp,boolean[][] isVisited){
if(i<0||j<0||i>=board.length||j>=board[0].length
||isVisited[i][j]==true||temp.children[board[i][j]-'a']==null)
return;
isVisited[i][j]=true;
temp=temp.children[board[i][j]-'a'];
if(temp.word!=null){
ans.add(temp.word);
temp.word=null;
}
backtrack(board,i,j+1,temp,isVisited);
backtrack(board,i+1,j,temp,isVisited);
backtrack(board,i,j-1,temp,isVisited);
backtrack(board,i-1,j,temp,isVisited);
isVisited[i][j]=false;
}
public TrieNode buildTrie(String[] words){
TrieNode root=new TrieNode();
for(String word : words){
TrieNode temp=root;
for(char c:word.toCharArray()){
int i=c-'a';
if(temp.children[i]==null){
temp.children[i]=new TrieNode();
}
temp=temp.children[i];
}
temp.word=word;
}
return root;
}
}