资讯专栏INFORMATION COLUMN

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

LuDongWei / 3450人阅读

摘要:复杂度时间空间为长度,为大小空间复杂度是是因为我用存信息,只动态地存当前的路径如果用来存信息的话空间复杂度就是时间复杂度对每个点都要作为起始点,对于每个起始点,拓展一次有四个可能性四个邻居,要拓展次长度为。思路暴力搜索带走。

Word Search I

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.

DFS 复杂度

O(MN*4^K ) 时间 O(K) 空间
K为word长度, M*N为board大小
空间复杂度是K是因为我用HashSet存visited信息,只动态地存当前dfs的路径;如果用boolean[][]来存visited信息的话空间复杂度就是O(MN)
时间复杂度:对每个点都要作为起始点dfs,对于每个起始点,拓展一次有四个可能性(四个邻居),要拓展k次(word长度为k)。

思路

暴力搜索带走。

注意

visited可以有多种实现方法:

boolean[ ] [ ] visited

HashSet visited,用个小trick把二维坐标转化为一维

二维转一维:(x,y) -> index : index = x * col + y

一维转二维:index -> (x,y) : x = index / col; y = index % col;

直接修改board数组,将访问过的格子改成特定字符比如 "#" 或者 "$"等

代码
public class Solution {
    public boolean exist(char[][] board, String word) {
        HashSet visited = new HashSet();
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (hasPath(0, i, j, word, board, visited))
                    return true;
            }
        }
        return false;
    }
    //找(x,y)是不是这个词
    public boolean hasPath(int pos, int x, int y, String word, char[][] board, HashSet visited) {
        if (pos == word.length() - 1) {
            if (board[x][y] == word.charAt(pos))
                return true;
            else
                return false;
        }
        else {
            char target = word.charAt(pos);
            if (target == board[x][y]) {
                visited.add(x * board[0].length + y);
                int[] dirx = {0, 0, 1, -1};
                int[] diry = {1, -1, 0, 0};
                for (int i = 0; i < 4; i++) {
                    int newx = x + dirx[i];
                    int newy = y + diry[i];
                    if (isValid(newx, newy, board) && !visited.contains(newx * board[0].length + newy)) {
                            if (hasPath(pos + 1, newx, newy, word, board, visited))
                                return true;
                    }
                }
                visited.remove(x * board[0].length + y);
                return false;
            }
            else {
                return false;
            }
        }
    }
    public boolean isValid(int x, int y, char[][] board) {
        if (x >= 0 && x <= board.length - 1 && y >= 0 && y <= board[0].length - 1)
            return true;
        else
            return false;
    }
}
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"].

Trie + DFS 复杂度

O(MN*4^K ) 时间 O(MN) 空间
K为word最大长度, M*N为board大小
空间复杂度:用boolean[][]来存visited信息
时间复杂度:对每个点都要作为起始点dfs,对于每个起始点,拓展一次有四个可能性(四个邻居),要拓展k次(word最大长度为k)。

思路

要用trie,拿trie来dfs
先建trie,然后在board里搜所有trie里的word

递归函数接口:

public void hasPath(int x, int y, 
                    char[][] board, 
                    boolean[][] visited, 
                    Trie trie, 
                    StringBuilder sb, 
                    List result)

满足的property:在进入hasPath的一刹那,1.(x,y)还没有访问;2.(x,y)是valid的而且还没有被访问过;3.此时的dfs快照是sb和visited

注意

尽管visited可以有多种实现方法,这一题用1,3都可以,用2就会超时:

boolean[ ] [ ] visited

HashSet visited,用个小trick把二维坐标转化为一维

二维转一维:(x,y) -> index : index = x * col + y

一维转二维:index -> (x,y) : x = index / col; y = index % col;

直接修改board数组,将访问过的格子改成特定字符比如 "#" 或者 "$"等

代码

Trie Utility:

class Trie {
    private static final int R = 26;
    TrieNode root = new TrieNode();
    
    private static class TrieNode {
        private boolean isWord = false;
        private TrieNode[] children = new TrieNode[R];
    }
    
    public void insert(String word) {
        TrieNode cur = root;
        for (int i = 0; i < word.length(); i++) {
            if (cur.children[word.charAt(i) - "a"] == null) {
                cur.children[word.charAt(i) - "a"] = new TrieNode();
            }
            cur = cur.children[word.charAt(i) - "a"];
            if (i == word.length() - 1)
                cur.isWord = true;
        }
    }
    
    public boolean search(String word) {
        TrieNode cur = root;
        for (int i = 0; i < word.length(); i++) {
            if (cur.children[word.charAt(i) - "a"] == null)
                return false;
            else {
                if (i == word.length() - 1)
                    return cur.children[word.charAt(i) - "a"].isWord;
                else {
                    cur = cur.children[word.charAt(i) - "a"];
                }
            }
        }
        return false;
    }
    
    public boolean startsWith(String word) {
        TrieNode cur = root;
        for (int i = 0; i < word.length(); i++) {
            if (cur.children[word.charAt(i) - "a"] == null) {
                return false;
            }    
            cur = cur.children[word.charAt(i) - "a"];
        }
        return true;
    }
    
}

主程序

public class Solution {
    public List findWords(char[][] board, String[] words) {
        List result = new ArrayList();
        Trie trie = new Trie();
        boolean[][] visited = new boolean[board.length][board[0].length];
        for (String str : words)
            trie.insert(str);
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                //带着当前的string去explore(i,j)这个点
                hasPath(i, j, board, visited, trie, new StringBuilder(), result);
            }
        }
        return result;
    }
    //x,y是合法的,sb里存得也是合法的,(x,y)还没有explore
    public void hasPath(int x, int y, char[][] board, boolean[][] visited, Trie trie, StringBuilder sb, List result) {
        //explore (x,y)
        sb.append(board[x][y]);
        visited[x][y] = true;
        //does (x,y) make sense? do this only when it does
        if (trie.startsWith(sb.toString())) {
            if (trie.search(sb.toString())) {
                if (!result.contains(sb.toString()))
                    result.add(sb.toString());
            }
            int[] dirx = {0,0,1,-1};
            int[] diry = {1,-1,0,0};
            for (int i = 0; i < 4; i++) {
                int newx = x + dirx[i];
                int newy = y + diry[i];
                if (isValid(newx, newy, board) && !visited[newx][newy]) {
                    hasPath(newx, newy, board, visited, trie, sb, result);
                }
            }
        }
        //finished exploring (x,y),let"s go back explore another one
        visited[x][y] = false;
        sb.deleteCharAt(sb.length() - 1);
    }
    public boolean isValid(int x, int y, char[][] board) {
        if (x >= 0 && x <= board.length - 1 && y >= 0 && y <= board[0].length - 1)
            return true;
        else
            return false;
    }
}

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

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

相关文章

  • [Leetcode] Word Search 单词搜索

    摘要:我们可以先用待查单词建立一个字典树,这样我们在从矩阵中某个点开始深度优先搜索时,可以直接用字典树判断当前组成的字符串是否是某个单词的前缀。字典树同样也提供接口,所以同样可以用于判断是否已经搜索到这个词了。 Word Search I 更新的思路与解法请访问:https://yanjia.me/zh/2018/11/... Given a 2D board and a word, f...

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

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

    张汉庆 评论0 收藏0
  • 6-9月技术文章汇总

    摘要:分布式的管理和当我在谈论架构时我在谈啥状态码详解无状态协议和请求支持哪些方法分层协议栈有哪些数据结构运用场景说说你常用的命令为什么要有包装类面向对象的特征是啥是啥有什么好处系统设计工程在线诊断系统设计与实现索引背后的数据结构及算法原理软技能 HTTP 【HTTP】分布式session的管理 【HTTP】Cookie和Session 【HTTP】当我在谈论RestFul架构时我在谈啥?...

    miya 评论0 收藏0
  • Word Break I, II &amp; Concatenated Words

    摘要:所以现在里面应该存可以使长度为所有可能的里的最后一个。有两种写法,一个就是直接写成数组的形式,不能形成的。结束之后,第二步就是通过里面保存的,一步一步回溯找到所有结果。直接的会超时,考虑记忆化搜索。所以事先对排序。 Word Break 链接:https://leetcode.com/problems... 这种找一个词由多个词组成的题,是拿dp或者dfs来解,dp本质上其实也是dfs...

    sunsmell 评论0 收藏0
  • LeetCode 精选TOP面试题【51 ~ 100】

    摘要:有效三角形的个数双指针最暴力的方法应该是三重循环枚举三个数字。总结本题和三数之和很像,都是三个数加和为某一个值。所以我们可以使用归并排序来解决这个问题。注意因为归并排序需要递归,所以空间复杂度为 ...

    Clect 评论0 收藏0

发表评论

0条评论

LuDongWei

|高级讲师

TA的文章

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