LeetCode题库-212题-单词搜索2

题目描述

代码展示

思路一(超时)

看到这个题目的第一反应,我就想以单词为基础,在网格区域去找,找到了就加入结果当中,算是一种暴力的解法吧。首先以每个单词为基础去找,遍历每个网格,如果网格的字符与单词的首字符匹配,就DFS去深度遍历,DFS按照每个方向去找,这里找的时候要避免重复访问,要设定一个isvisit数组判断,直到找到目标单词为止。但是这种思路在力扣上面有个比较恶心的测试案例给卡住了,那个案例单词有很多,而且全是重复的前缀的字符串,比如“aaaaay”和“aaaaaz”这种,就导致这种方法重复搜索了很多,最后超时了,但是代码展示在下面

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class t211_1 {//以单词为基准,在网格中去搜索每个单词的暴力方法
    HashSet<String> ret=new HashSet<>();//避免重复
    List<String> res=new ArrayList<>();//结果
    boolean[][] isvisit=new boolean[12][12];//判断是否重复搜索
    int[][] dirs=new int[][]{{1,0},{-1,0},{0,1},{0,-1}};//方向
    public List<String> findWords(char[][] board, String[] words) {
        for (String word : words) {//按照每个单词搜索
            for (int j = 0; j < board.length; j++) {
                for (int k = 0; k < board[0].length; k++) {
                    if (board[j][k] == word.charAt(0)) {//网格字符与单词第一个字符匹配,就以该网格为基础,深度搜索
                        isvisit[j][k] = true;
                        dfs(board, word, j, k, 1);//深度搜索
                        isvisit[j][k] = false;
                    }
                    if (ret.contains(word)) break;//搜索成功,剪枝到下一个单词
                }
                if (ret.contains(word)) break;
            }
        }
        res.addAll(ret);//转为List<String>
        return res;
    }
    public void dfs(char[][] board,String word,int x,int y,int index){
        if(index==word.length()){//成功找到
            ret.add(word);
            return;
        }
        for(int[] dir:dirs){//每个方向一次探索
            int dx=x+dir[0],dy=y+dir[1];
            //超出网格区域
            if(dx<0||dx>=board.length||dy<0||dy>=board[0].length) continue;
            //重复访问
            if(isvisit[dx][dy]) continue;
            //字符匹配
            if(board[dx][dy]==word.charAt(index)){
                index++;
                isvisit[dx][dy]=true;
                //继续深度搜索
                dfs(board,word,dx,dy,index);
                isvisit[dx][dy]=false;
                index--;
            }
        }
    }
}

测试案例如下(部分):

思路二(较慢)

第二种思路也是暴力算法的一种吧,就是以每个网格为基础,依次去深度遍历,把所有可能的单词全部找出来,然后看看是否和words数组里面的单词匹配。这种方法就可以避免上面那个思路的那个恶心测试案例,因为它是以网格数为基础的深度遍历,和单词的数量关系不大,也不会重复去遍历。具体的思路如下:

import java.util.*;

public class t211_2 {//将所有可能的连起来的串全部找到,看看符不符合要求,暴力算法
    //放在外面避免写dfs的时候带在后面,是全局变量
    Set<String> set = new HashSet<>();//放单词的
    List<String> ans = new ArrayList<>();//放结果的
    char[][] newboard;
    int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};//方向
    int n, m;
    boolean[][] vis = new boolean[12][12];//判断是否访问过,因为网格长度最大12

    public List<String> findWords(char[][] board, String[] words) {
        newboard = board;
        m = newboard.length; n = newboard[0].length;
        //初始化set
        set.addAll(Arrays.asList(words));
        //字符串变量
        StringBuilder sb = new StringBuilder();
        //每个店为起点
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                vis[i][j] = true;
                sb.append(newboard[i][j]);
                //深度遍历
                dfs(i, j, sb);
                vis[i][j] = false;
                sb.deleteCharAt(sb.length() - 1);
            }
        }
        return ans;
    }

    void dfs(int i, int j, StringBuilder sb) {
        //因为单词最长为10,超过10的不用考虑
        if (sb.length() > 10) return ;
        //找到了符合要求的单词
        if (set.contains(sb.toString())) {
            ans.add(sb.toString());
            set.remove(sb.toString());
        }
        //每个方向一次遍历
        for (int[] d : dirs) {
            int dx = i + d[0], dy = j + d[1];
            //超出边界
            if (dx < 0 || dx >= m || dy < 0 || dy >= n) continue;
            //重复访问
            if (vis[dx][dy]) continue;
            vis[dx][dy] = true;
            sb.append(newboard[dx][dy]);
            dfs(dx, dy, sb);
            vis[dx][dy] = false;
            sb.deleteCharAt(sb.length() - 1);
        }
    }
}

思路三(DFS+回溯+字典树)

这里题目设定的意义就是字典树,因为他可以避免那个前缀相同的单词,避免重复遍历。力扣对字典树的解释是:前缀树(字典树)是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。前缀树可以用 O(∣S∣) 的时间复杂度完成如下操作,其中 ∣S∣ 是插入字符串或查询前缀的长度:

1.向前缀树中插入字符串 word;

2.查询前缀串 prefix 是否为已经插入到前缀树中的任意一个字符串 word 的前缀;

在初始化的时候,在字典树的根节点处设置好字符串变量s,表示按照之前的路径可以成功找到单词s,这也就是提前设定递归出口。然后按照网格里面的字符,依次深度遍历,在字典树中找,看看网格的字符是否与字典树的字符相匹配,匹配就继续深度遍历,否则就换方向或者返回。以上是大致思路,具体代码如下:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class t211_3 {
    //字典树
    class TrieNode {
        String s;//尾节点单词标记,递归出口
        TrieNode[] tns = new TrieNode[26];//当前节点的子节点,因为字符就26个
    }
    //在字典树中创建一个单词
    void insert(String s) {
        TrieNode p = root;
        for (int i = 0; i < s.length(); i++) {//每个字符一次遍历
            //字符在子节点数组中的位置
            int u = s.charAt(i) - 'a';
            //不存在就新建
            if (p.tns[u] == null) p.tns[u] = new TrieNode();
            //依次递归创建
            p = p.tns[u];
        }
        //尾节点更新单词
        p.s = s;
    }

    Set<String> set = new HashSet<>();
    char[][] newboard;
    int n, m;
    TrieNode root = new TrieNode();
    int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
    boolean[][] vis = new boolean[12][12];

    public List<String> findWords(char[][] board, String[] words) {
        newboard = board;
        m = newboard.length; n = newboard[0].length;
        //初始化字典树
        for (String w : words) insert(w);
        //每个网格依次遍历
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int u = newboard[i][j] - 'a';
                //看看有没有以该网格字符为头字符的单词,有就深度遍历
                if (root.tns[u] != null) {
                    vis[i][j] = true;
                    dfs(i, j, root.tns[u]);
                    vis[i][j] = false;
                }
            }
        }
        return new ArrayList<>(set);
    }
    //深度遍历
    void dfs(int i, int j, TrieNode node) {
        //到尾节点了,表示找到了该单词
        if (node.s != null) set.add(node.s);
        for (int[] d : dirs) {
            int dx = i + d[0], dy = j + d[1];
            if (dx < 0 || dx >= m || dy < 0 || dy >= n) continue;
            if (vis[dx][dy]) continue;
            //依次遍历,看看该方向的字符在不在字典树里面出现
            int u = newboard[dx][dy] - 'a';
            if (node.tns[u] != null) {
                vis[dx][dy] = true;
                dfs(dx, dy, node.tns[u]);
                vis[dx][dy] = false;
            }
        }
    }
}

  • 11
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Leetcode题库是一个包含了各种算法和数据结构问的在线题库,供程序员练习和提升技能使用。这个题库中涵盖了大量的目,包括但不限于图遍历、最短路径、有效的括号、字符串解码、Z字形变换、最长公共前缀、字符串相乘、颠倒字符串中的单词、比较版本号、反转字符串、压缩字符串、验证IP地址等等。它提供了java实现的leetcode解法,这些解法的代码规范,可读性良好,并且其中的解法思想并不受语言限制。通过解决这些目,程序员可以提高自己的算法和编码能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [leetcode题库-leetcode-java:LeetcodeJava代码](https://download.csdn.net/download/weixin_38661100/19901136)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [LeetCode算法整理(200左右)](https://blog.csdn.net/qq_38253797/article/details/126827454)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值