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

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可以有多种实现方法:

  1. boolean[ ] [ ] visited

  2. HashSet<Integer> visited,用个小trick把二维坐标转化为一维

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

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

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

代码

public class Solution {
    public boolean exist(char[][] board, String word) {
        HashSet<Integer> visited = new HashSet<Integer>();
        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<Integer> 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<String> result)

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

注意

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

  1. boolean[ ] [ ] visited

  2. HashSet<Integer> visited,用个小trick把二维坐标转化为一维

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

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

  3. 直接修改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<String> findWords(char[][] board, String[] words) {
        List<String> result = new ArrayList<String>();
        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<String> 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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值