单词搜索2-LeetCode-困难

贼难

问题描述

给定一个二维网格 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;
    }


}
发布了149 篇原创文章 · 获赞 137 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zhang_ye_ye/article/details/98470814