- 单词搜索 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(前缀树)。
1.借助L79的代码
class Solution {
public List<String> findWords(char[][] board, String[] words) {
//转化为L79的查找问题
List<String> list = new ArrayList<>();
for(String st : words){
if(exist(board, st)){
list.add(st);//这里就直接使用79代码
}
}
return list;
}
int m, n;
int mp[][] ={{1, 0}, {-1, 0}, {0, -1}, {0, 1}};
public boolean exist(char[][] board, String word) {
m = board.length;
n = board[0].length;
if(m == 0) return false;
boolean flag[][] = new boolean[m][n];
for(int i = 0; i < m; i++){//对所有的方格点进行遍历
for(int j = 0; j < n; j++){
if(dfs(board, word, i, j, flag, 0)) return true;
}
}
return false;
}
public boolean dfs(char[][] board, String word, int i, int j, boolean[][] flag, int index){
if(index == word.length() - 1){//与常规的判断方式有些不同,如果直接写index== word.length,由于下面有好多判断条件,其实走不到dfs
//比如只有一个字母的时候
return word.charAt(index) == board[i][j];
}
if(board[i][j] == word.charAt(index)){
//四个方向依次判断
flag[i][j] = true;//先标记
for(int k = 0; k < 4; k++){
int newX = i + mp[k][0];
int newY = j + mp[k][1];
if(newX >= 0 && newX < m && newY >= 0 && newY < n && flag[newX][newY] == false){
if(dfs(board, word, newX, newY, flag, index + 1)) return true;
}
}
flag[i][j] = false;
}
return false;
}
}
3.遍历过程中判断是否遇到了某个单词,我们可以事先把所有单词存到前缀树中。这样的话,如果当前走的路径不是前缀树的前缀,我们就可以提前结束了。如果是前缀树的中的单词,我们就将其存到结果中。
至于实现的话,我们可以在遍历过程中,将当前路径的单词传进函数,然后判断当前路径构成的单词是否是在前缀树中出现即可。
我们可以将前缀树融合到我们的算法中,递归中去传递前缀树的节点,判断当前节点的孩子是否为 null,如果是 null 说明当前前缀不存在,可以提前结束。如果不是 null,再判断当前节点是否是单词的结尾,如果是结尾直接将当前单词加入。
不能通过,不知道原因
class Trie {
Node root;
public Trie(){
root = new Node();
}
public void insert(String word) {
Node now = root;//每次获取当前的第一个节点
char ch[] = word.toCharArray();
int len = ch.length;
for(int i = 0; i < len; i++){
if(now.next[ch[i] - 'a'] == null){//没有就新建一个
now.next[ch[i] - 'a'] = new Node();
}
now= now.next[ch[i] - 'a'];//进入下一个节点
}
now.word = word;
}
}
class Solution {
public List<String> findWords(char[][] board, String[] words) {
Trie trie = new Trie();
List<String> list = new ArrayList<>();
for(String st : words){//全部加入前缀树
trie.insert(st);
}
boolean flag[][] = new boolean[m][n];
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
dfs(board, i, j, flag, list,trie.root);
//参数要重新设置,不需要String,只需要与前缀树比较即可
}
}
return list;
}
int m, n;
int[][] mp = {{1, 0},{-1, 0}, {0, 1}, {0, -1}};
public void dfs( char[][] board, int i, int j, boolean[][] flag, List<String> list, Node node){//注意是Node,不是Trie
m = board.length;//忘记给m, n赋值
n = board[0].length;
if(i >= 0 && i < m && j >= 0 && j < n && flag[i][j] == false && node.next[(board[i][j] - 'a')] != null){
node = node.next[(board[i][j] - 'a')];
if(node.word != null){
//此时已经到达一个单词的结尾
list.add(node.word);
node.word = null;//防止重复加入
}
flag[i][j] = true;
for(int k = 0; k < 4; k++){
int newx = i + mp[k][0];
int newy = j + mp[k][1];
dfs(board, newx, newy, flag, list, node);
}
flag[i][j] = false;
}
}
}