资讯专栏INFORMATION COLUMN

[Leetcode] Word Search 单词搜索

objc94 / 2104人阅读

摘要:我们可以先用待查单词建立一个字典树,这样我们在从矩阵中某个点开始深度优先搜索时,可以直接用字典树判断当前组成的字符串是否是某个单词的前缀。字典树同样也提供接口,所以同样可以用于判断是否已经搜索到这个词了。

Word Search I 更新的思路与解法请访问:https://yanjia.me/zh/2018/11/...

Given a 2D board and a word, find if the word exists in the grid.

The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

For example, Given board =

[
  ["ABCE"],
  ["SFCS"],
  ["ADEE"]
]

word = "ABCCED", -> returns true, word = "SEE", -> returns true, word = "ABCB", -> returns false.

深度优先搜索 复杂度

时间 O(N^2) 空间 O(N)

思路

基本思路很简单,对矩阵里每个点都进行一次深度优先搜索,看它能够产生一个路径和所给的字符串是一样的。重点在如何优化搜索,避免不必要的计算。比如我们一个方向的搜索中已经发现了这个词,那我们就不用再搜索。另外,为了避免循环搜索,我们还要将本轮深度优先搜索中搜索过的数字变一下,等递归回来之后再变回来。实现这个特性最简单的方法就是异或上一个特定数,然后再异或回来。

代码
public class Solution {
    public boolean exist(char[][] board, String word) {
        if(board.length == 0) return false;
        for(int i = 0; i < board.length; i++){
            for(int j = 0; j < board[0].length; j++){
                // 从i,j点作为起点开始搜索
                boolean isExisted = search(board, i, j, word, 0);
                if(isExisted) return true;
            }
        }
        return false;
    }
    
    private boolean search(char[][] board, int i, int j, String word, int idx){
        if(idx >= word.length()) return true;
        if(i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(idx)) return false;
        // 将已经搜索过的字母标记一下,防止循环。只要变成另外一个字符,就不会再有循环了。
        board[i][j] ^= 255;
        boolean res = search(board, i-1, j, word, idx+1) || search(board, i+1, j, word, idx+1) || search(board, i, j-1, word, idx+1) || search(board, i, j+1, word, idx+1);
        // 再次异或255就能恢复成原来的字母
        board[i][j] ^= 255;
        return res;
    }
}
Word Search II

Given a 2D board and a list of words from the dictionary, find all words in the board.

Each word must be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.

For example, Given words = ["oath","pea","eat","rain"] and board =

[
  ["o","a","a","n"],
  ["e","t","a","e"],
  ["i","h","k","r"],
  ["i","f","l","v"]
] 

Return ["eat","oath"].

字典树 复杂度

时间 O(N^2logN) 空间 O(N)

思路

如果还像一中那样,对每个词进行一遍Word Search I,那复杂度就太高了。我们可以先用待查单词建立一个字典树,这样我们在从矩阵中某个点开始深度优先搜索时,可以直接用字典树判断当前组成的字符串是否是某个单词的前缀。如果是某个单词的前缀,再继续搜索下去。字典树同样也提供search接口,所以同样可以用于判断是否已经搜索到这个词了。

注意

因为结果中不能有重复,我们可以在加入结果前先判断是否已经加过该结果,也可以稍微修改一下字典树的search方法,使得每次我们搜索到一个单词时,将其isEnd的标记置为false,这样下次就不会搜索到这个词了。

代码
public class Solution {
    
    List res;
    Trie trie;
    
    public List findWords(char[][] board, String[] words) {
        res = new LinkedList();
        trie = new Trie();
        // 构建字典树
        for(String word : words){
            trie.insert(word);
        }
        // 深度优先搜索
        for(int i = 0; i < board.length; i++){
            for(int j = 0; j < board[0].length; j++){
                helper(board, String.valueOf(board[i][j]), i, j);
            }
        }
        return res;
    }
    
    private void helper(char[][] board, String tmp, int i, int j){
        // 如果字典树中存在该字符串,说明找到了单词
        if(trie.search(tmp)){
            if(!res.contains(tmp)) res.add(tmp);
        }
        // 为了防止循环,将当前字母改变一下
        board[i][j] ^= 255;
        // 对四个方向搜索,如果当前字符串是前缀就继续搜索
        if(j < board[0].length - 1){
            String next = tmp + board[i][j+1];
            if(trie.startsWith(next)) helper(board, next, i, j + 1);
        }
        if(i > 0){
            String next = tmp + board[i-1][j];
            if(trie.startsWith(next)) helper(board, next, i - 1, j);
        }
        if(i < board.length - 1){
            String next = tmp + board[i+1][j];
            if(trie.startsWith(next)) helper(board, next, i + 1, j);
        }
        if(j > 0){
            String next = tmp + board[i][j-1];
            if(trie.startsWith(next)) helper(board, next, i, j - 1);
        }
        board[i][j] ^= 255;
    }
    
    // 字典树的节点
    public class TrieNode {
        Character c;
        HashMap children;
        boolean isEnd;
        public TrieNode(char c){
            this.c = c;
            this.isEnd = false;
            this.children = new HashMap();
        }
    }
    
    // 字典树
    public class Trie {
        TrieNode root;
        
        public Trie(){
            this.root = new TrieNode("r");
        }
        
        public void insert(String str){
            TrieNode curr = root, next = null;
            int idx = 0;
            for(int i = 0; i < str.length(); i++){
                next = curr.children.get(str.charAt(i));
                if(next == null){
                    next = new TrieNode(str.charAt(i));
                }
                if(i == str.length() - 1){
                    next.isEnd = true;
                }
                curr.children.put(str.charAt(i), next);
                curr = next;
            }
        }
        
        public boolean search(String str){
            TrieNode res = searchNode(str);
            return res != null && res.isEnd ? true : false;
        }
        
        public boolean startsWith(String str){
            TrieNode res = searchNode(str);
            return res == null ? false : true;
        }
        
        private TrieNode searchNode(String str){
            TrieNode curr = root, next = null;
            for(int i = 0; i < str.length(); i++){
                next = curr.children.get(str.charAt(i));
                if(next == null){
                    return null;
                }
                curr = next;
            }
            return next;
        }
    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64461.html

相关文章

  • [Leetcode] Word Search I&II 二维字符矩阵查找单词

    摘要:复杂度时间空间为长度,为大小空间复杂度是是因为我用存信息,只动态地存当前的路径如果用来存信息的话空间复杂度就是时间复杂度对每个点都要作为起始点,对于每个起始点,拓展一次有四个可能性四个邻居,要拓展次长度为。思路暴力搜索带走。 Word Search I Given a 2D board and a word, find if the word exists in the grid. ...

    LuDongWei 评论0 收藏0
  • 前端 | 每天一个 LeetCode

    摘要:在线网站地址我的微信公众号完整题目列表从年月日起,每天更新一题,顺序从易到难,目前已更新个题。这是项目地址欢迎一起交流学习。 这篇文章记录我练习的 LeetCode 题目,语言 JavaScript。 在线网站:https://cattle.w3fun.com GitHub 地址:https://github.com/swpuLeo/ca...我的微信公众号: showImg(htt...

    张汉庆 评论0 收藏0
  • Leetcode】79.单词搜索

    摘要:题目给定一个二维网格和一个单词,找出该单词是否存在于网格中。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中相邻单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。 题目 给定一个二维网格和一个单词,找出该单词是否存在于网格中。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中相邻单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允...

    Caicloud 评论0 收藏0
  • Leetcode】79.单词搜索

    摘要:题目给定一个二维网格和一个单词,找出该单词是否存在于网格中。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中相邻单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。 题目 给定一个二维网格和一个单词,找出该单词是否存在于网格中。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中相邻单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允...

    ruicbAndroid 评论0 收藏0
  • [Leetcode] Word Break 单词分解

    摘要:所以只要验证满足这个条件,我们则可以确定这个较长的字符串也是可分解的。同时,我们用数组记录下字符串长度递增时可分解的情况,以供之后使用,避免重复计算。当遍历完这个词典并找出所有以第一个字母开头的词以后,我们进入下一轮搜索。 Word Break I Given a string s and a dictionary of words dict, determine if s can ...

    Ververica 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<