贼难
问题描述
给定一个二维网格 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(前缀树)。
解决思路
这个题考查的就是对Trie树的应用
1、先将所给矩阵所包含的单词装进Trie树里
- 以矩阵中的一个元素为出发点,对它周围四个方向遍历
2、然后查找所给单词
3、但是没有这么简单,题目要求需要优化回溯算法
- 根据所给的单词进行遍历,如{“eat”,“oath”},那么就只遍历第一个字符为’e’,'o’的元素,以此类推
代码实现
package solution;
import java.util.*;
class Solution5 {
public static void main(String[] args) {
char[][] chars=new char[][]{{'o','a','a','n'},{'e','t','a','e'},{'i','h','k','r'},{'i','f','l','v'}};
String[] strings=new String[]{"oath","pea","eat","rain"};
List<String> list=new Solution5().findWords(chars,strings);
System.out.println(list.toString());
}
public List<String> findWords(char[][] board, String[] words) {
MyTrie myTrie=new MyTrie();
myTrie.add(board,words);
List<String> list=new ArrayList<>();
for (int i=0;i<words.length;i++){
if(myTrie.search(words[i])){
//查找单词
list.add(words[i]);
}
}
return list;
}
}
class MyTrie{
private Node root;
private class Node{
private Node[] next;
public Node(){
next=new Node[26];
}
}
public MyTrie(){
root=new Node();
}
public void add(char[][] board,String[] words){
//这是一个用来做标记的矩阵,遍历过的元素就为true
boolean[][] flag=new boolean[board.length][board[0].length];
for (int i=0;i<board.length;i++){
for (int j=0;j<board[0].length;j++){
//对矩阵中的每个元素遍历
add(root,board,i,j,flag,0,words);
}
}
}
private void add(Node node,char[][] board,int i,int j,boolean[][] flag,int index,String[] words){
if(flag[i][j]){
return;
}
//设为true说明已经遍历过
flag[i][j]=true;
//当前字符
char cc=board[i][j];
//用来存储下标index对应的字符串的字符
Set<Character> set=new HashSet<>();
List<String> ss=new ArrayList<>();
//遍历所有字符串
for (String s:words){
if(index<s.length()) {
set.add(s.charAt(index));
//如果这个字符串符合,为下一次遍历做准备
if(s.charAt(index)==cc){
ss.add(s);
}
}
}
//如果所有的字符串都不满足,退出遍历
if(!set.contains(cc)){
flag[i][j]=false;
return;
}
words=new String[ss.size()];
for (int k=0;k<ss.size();k++){
words[k]=ss.get(k);
}
//如果对应位置,直接下一条,刚开始没有判断,直接new,导致在这里出现问题
if(node.next[cc-'a']!=null){
index++;
node=node.next[cc-'a'];
}else {
index++;
node.next[cc - 'a'] = new Node();
node = node.next[cc - 'a'];
}
//向左
if(j-1>=0){
add(node,board,i,j-1,flag,index,words);
}
//向右
if (j+1<board[0].length){
add(node,board,i,j+1,flag,index,words);
}
//向上
if (i-1>=0){
add(node,board,i-1,j,flag,index,words);
}
//向下
if(i+1<board.length){
add(node,board,i+1,j,flag,index,words);
}
//遍历完之后,一定要将这个加上,为下一个元素遍历做准备
flag[i][j]=false;
}
//查找
public boolean search(String word){
Node cur=root;
for (int i=0;i<word.length();i++){
char c=word.charAt(i);
if(cur.next[c-'a']==null){
return false;
}
cur=cur.next[c-'a'];
}
return true;
}
}