单词搜索 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(前缀树)。
思路:前缀树和BFS
我们通过对存放单词的数组每个每个元素进行建树,然后在对每个矩阵中的每个节点进行BFS访问前缀数。
建树:对每个矩阵中的结点从根节点开始建树,到了最底部(也就是一个单词的最后一个字符)用这个前缀树的中结点保存这个字符串
BFS:关键在于对于被访问过的结点的矩阵的传递问题,在BFS中我们从一个结点的四周出发,每次进入一个方向的flag矩阵应该是一样的都是基于上一节点的。
所以我们要及时将上一节点的flag矩阵复制过来。复制中有个问题就是对应二位数组的复制我们不能直接help=flag.clone()因为对于一个二维数组来说我们直接操作数组名,就是将一个个一位数组的引用给了别的数组(数组就是一个对象)我们在递归中对这种数组操作是会直接影响在内存中被引用的数组也就是会比带回,会被下一个外部的BFS所使用,从而造成BUG。但是对于每个一位数组如果里面的数据是基本类型我们就是复制过来值得部分。所以我们应该对应每个二维数组的一维数组部分单独拿出来clone。
class Solution {
class Node{ //定义前缀树的结点
boolean f=false; //f作为标记标记这个单词是否被添加过
String word; //保存单词
Node[] kids=new Node[26]; //26个子树
}
Node root=new Node();
public List<String> findWords(char[][] board, String[] words) {
for(String str:words) { //对单词建树
Node node=root;
for(char c:str.toCharArray()) {
if(node.kids[c-'a']==null) {
node.kids[c-'a']=new Node();
}
node=node.kids[c-'a'];
}
node.word=str; //将对应的叶子节点的word赋值
}
List<String> list=new ArrayList<>();
//对矩阵的单词进行搜索
for(int i=0;i<board.length;i++) {
for(int j=0;j<board[0].length;j++) {
//每次都新创建一个flag作为访问过的标记
boolean[][] flag=new boolean[board.length][board[0].length];
BFS(board,i,j,root,list,flag);
}
}
return list;
}
private void BFS(char[][] board, int i, int j, Node node, List<String> list, boolean[][] flag) {
if(i<0||i>=board.length||j<0||j>=board[0].length)//越界
return ;
if(flag[i][j]==true) //之前访问过这个单词
return ;
boolean help[][]=new boolean[flag.length][flag[0].length];
//新的访问过的标记矩阵
for(int k=0;k<board.length;k++) {
help[k]=flag[k].clone(); //深拷贝不能直接 help=flag.clone()会出现带回的问题
}
help[i][j]=true; //将该点标记
char c=board[i][j];
node=node.kids[c-'a'];
if(node==null) //这个点不是任何保存了的单词的前缀
return ;
if(node.word!=null&&node.f==false) {//有单词并且没有被add过
list.add(node.word);
node.f=true;
}
//对上下左右进行BFS
BFS(board,i+1,j,node,list,help);
BFS(board,i,j+1,node,list,help);
BFS(board,i-1,j,node,list,help);
BFS(board,i,j-1,node,list,help);
}
public static void main(String[] ARGS) {
char[][] board= {{'a','a'}};
String []words= {"aa"};
List<String> list=new Solution().findWords(board, words);
for(String str:list) {
System.out.println(str);
}
}
}