题干
给定一个二维网格 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,反复查询很浪费事件
于是使用字典树先来保存board里的字,提高效率,其他都是一样的
直接看代码
class Solution {
public int[] dx={1,-1,0,0};
public int[] dy={0,0,1,-1};
private static class Trie{ //字典树
String word; //单词结束标志,到这里说明已经可以组成一个单词了。
Trie[] next=new Trie[26]; //每个节点都有26个节点
Trie append(char ch){ //拼接字符
if(next[ch-'a']!=null){
return next[ch-'a']; //有则直接返回节点
}
next[ch-'a']=new Trie(); //没有就新建节点
return next[ch-'a'];
}
}
public boolean[][] vis; //判断是否参观过
public List<String> res;
public List<String> findWords(char[][] board, String[] words) {
int m=board.length;
int n=board[0].length;
Trie root=new Trie();
for(String word:words){ //先把单词存入字典树当中
Trie cur=root;
char[] wd=word.toCharArray();
for(char c:wd){
cur=cur.append(c);
}
cur.word=word; //结束标志,说明到这里已经可以组成一个单词
}
res=new ArrayList<String>();
vis=new boolean[m][n];
for(int i=0;i<m;++i){ //对board每一个点都进行检索
for(int j=0;j<n;++j){
dfs(root,i,j,board,m,n);
}
}
Collections.sort(res); //需要对结果进行排序
return new ArrayList<String>(res);
}
public void dfs(Trie cur,int x,int y,char[][] board,int m,int n){
if(x<0||y<0||x>=m||y>=n||vis[x][y]){
return;
} //边界返回
cur=cur.next[board[x][y]-'a']; //延伸下一个节点
vis[x][y]=true; //把当前设置为走过 不可重复走
if(cur!=null){ //如果当前不为null的话 可以继续检索
if(cur.word!=null){ //说明到这里已经可以组成一个单词了
res.add(cur.word);
cur.word=null; //变成null是为了防止重复加入单词
}
for(int i=0;i<4;++i){
dfs(cur,x+dx[i],y+dy[i],board,m,n); //四个方向检索
}
}
vis[x][y]=false;
}
}
leetcode上更快的解法
class Trie_Node{
public int endFlagIndex=-1,children_num=0;
public Trie_Node parent=null;
public char value;
public Trie_Node children[]=new Trie_Node[26];
}
class Trie {
Trie_Node root;
/** Initialize your data structure here. */
public Trie() {
root = new Trie_Node();
}
/** Inserts a word into the trie. */
public void insert(String word) {
int n=word.length();
Trie_Node p=root;
for(int i=0;i<n;i++){
char c=word.charAt(i);
int seq=c-'a';
if(p.children[seq]==null){
p.children[seq]=new Trie_Node();
p.children[seq].parent=p;
p.children_num++;
}
p=p.children[seq];
}
p.endFlagIndex=0;
}
/** Inserts a word into the trie. */
public void insert(String word,int index) {
int n=word.length();
Trie_Node p=root;
for(int i=0;i<n;i++){
char c=word.charAt(i);
int seq=c-'a';
if(p.children[seq]==null){
p.children[seq]=new Trie_Node();
p.children[seq].value=c;
p.children[seq].parent=p;
p.children_num++;
}
p=p.children[seq];
}
p.endFlagIndex=index;
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
int n=word.length();
Trie_Node p=root;
int i=0;
while(i<n){
char c=word.charAt(i);
int seq=c-'a';
if(p.children[seq]==null){
return false;
}
p=p.children[seq];
++i;
}
if(p.endFlagIndex!=-1)
return true;
else
return false;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
int n=prefix.length();
Trie_Node p=root;
int i=0;
while(i<n){
char c=prefix.charAt(i);
int seq=c-'a';
if(p.children[seq]==null){
return false;
}
p=p.children[seq];
++i;
}
if(i==n)
return true;
else
return false;
}
public void delete(Trie_Node node){
if(node.children_num>0){
node.endFlagIndex=-1;
return;
}
Trie_Node p=node.parent;
p.children[node.value-'a']=null;
p.children_num--;
if(p.parent!=null && p.endFlagIndex==-1)
delete(p);
else
return;
}
public Trie_Node find_same(Trie_Node node,char[][] board,int m,int n,int i,int j,boolean[][] check){
Trie_Node res=null;
char ch=board[i][j];
int seq=ch-'a';
if(node.children[seq]==null){
return null;
}
check[i][j] = true;
if(node.children[seq].endFlagIndex!=-1)
res=node.children[seq];
if(res == null && i>0 && !check[i-1][j])
res=find_same(node.children[seq],board,m,n,i-1,j,check);
if(res == null && j<(n-1) && !check[i][j+1])
res=find_same(node.children[seq],board,m,n,i,j+1,check);
if(res == null && i<(m-1) && !check[i+1][j])
res=find_same(node.children[seq],board,m,n,i+1,j,check);
if(res == null && j>0 && !check[i][j-1])
res=find_same(node.children[seq],board,m,n,i,j-1,check);
check[i][j] = false;
return res;
}
}
class Solution {
public List<String> findWords(char[][] board, String[] words) {
List<String> res=new ArrayList<>();
if(board.length==0 || board[0].length==0)
return res;
int m=board.length,n=board[0].length;
Trie trie=new Trie();
for(int i=0;i<words.length;i++)
trie.insert(words[i],i);
boolean[][] check;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
check = new boolean[m][n];
Trie_Node tmp_node = trie.find_same(trie.root,board,m,n,i,j,check);
if(tmp_node!=null){
res.add(words[tmp_node.endFlagIndex]);
trie.delete(tmp_node);
j--;
}
}
}
return res;
}
}