单词搜索 II · Word Search II答案解析

题目:原题见lintcode链接, 或者复制以下链接到浏览器
https://www.lintcode.com/problem/word-search-ii/description?_from=ladder&&fromId=161

132. Word Search II
Given a matrix of lower alphabets and a dictionary. Find all words in the dictionary that can be found in the matrix. A word can start from any position in the matrix and go left/right/up/down to the adjacent position. One character only be used once in one word. No same word in dictionary

Example
Example 1:

Input:["doaf","agai","dcan"],["dog","dad","dgdg","can","again"]
Output:["again","can","dad","dog"]
Explanation:
  d o a f
  a g a i
  d c a n
search in Matrix,so return ["again","can","dad","dog"].
Example 2:

Input:["a"],["b"]
Output:[]
Explanation:
 a
search in Matrix,return [].
Challenge
Using trie to implement your algorithm.

这道题有两种思路
1)遍历词典中的每个单词,check单词是否在矩阵中
2)遍历矩阵中每个单词,check是否在词典中
这两个思路优选选哪个?
一般选2),因为2)的效率比较高,1)的话要多次遍历矩阵

先通过一个例子模拟解法

matrix:
  d o a f
  a g a i
  d c a n
dictionary:
["dog","dad","dgdg","can","again"]

第一步,找到一个起点[0,0]位置上的字符d, 含有d前缀的词出现在dictionary里面,继续搜索下一个字符。 往右, d -> o ,没有do前缀的词,那么退回到上一级。往下, d -> a,存在含有da前缀的词,继续。 往右,d -> a ->g, 不存在,返回上一级。往下, d -> a -> d, dad是dictionary中的一个词,把这个词加到结果中。继续寻找, d -> a -> d -> c, 不存在,一层层返回到上级, 到起始位置d。 以d开头的单词搜索完毕。
接下来从[0,1]位置上的o开始, 重复第一步操作。
把matrix中的每一个字符作为起始点搜索。

整个搜索过程就是我们熟悉的深度优先搜索(depth priority search).

这道问题的另一个难点就是如何匹配matrix中字符组成的前缀与dictionary中的词。有两种方法:
1)hashset
2)trie(字典树)

方法一用hashset构建前缀set,比如上诉例子的前缀set就是{“d”, “do”, “da”, “dg”, “dgd”, “c”, “ca”, “a”, “ag”, “aga”, “agai”, dog",“dad”,“dgdg”,“can”,“again”}

方法二字典树
关于字典树的java实现可以参考下面两个链接

那么我们把思路总结一下

//建立字典树
//遍历矩阵中每个字符
//任意字符作为起始字符
//dfs搜索下一个字符
//如果当前字符不在字典树上,则退出
//如果当前字典树节点是叶子节点,把整条分支上的字符构成的单词加到结果中
//遍历当前位置的上下左右字符
//加上字符继续dfs

首先建立字典树
要建树,先建节点
label是每个节点上的字符
children就是一个用来存放下一级的子节点
isEnd表明是否是一个单词的终止节点。比如dictionary = [“dark”, “darkness”]. d -> a -> r -> k(isEnd=true) -> n -> e -> s ->s(isEnd=true)

class TrieNode {
    char label;
    Map<Character, TrieNode> children = new HashMap<>();
    boolean isEnd;
    
    public TrieNode(){
        
    }
    public TrieNode(char label) {
        this.label = label;
    }
    
}

构建字典树以及插入方法

class Trie {

    public TrieNode root;
    public Trie(){
        this.root = new TrieNode();
    }
    public void insert(String word){
        if(word == null || word.length() == 0){
            return;
        }
        TrieNode node = root;
        for(int i=0;i<word.length();i++){
            char ch = word.charAt(i);
            if(!node.children.containsKey(ch)){
                node.children.put(ch, new TrieNode(ch));
            }
            node = node.children.get(ch);
        }
        //注意在单词结尾isEnd = true
        node.isEnd = true;
        
    }

}

dfs代码实现

public class Solution {
    /**
     * @param board: A list of lists of character
     * @param words: A list of string
     * @return: A list of string
     */
    class Position {
        int x;
        int y;
        public Position(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
    //获得上下左右字符的位置
    int[] dx = {-1, 1, 0, 0};
    int[] dy = {0, 0, -1, 1};
    
    public List<String> wordSearchII(char[][] board, List<String> words) {
        // write your code here
        List<String> results = new ArrayList<>();
        if(board == null || board.length == 0 || board[0].length == 0|| words == null || words.size() == 0){
            return results;
        }
        //build a trie for dictionary
        Trie wordsTrie = new Trie();
        for(String word: words){
            wordsTrie.insert(word);
        }
        
        //find all possible words in board
        boolean[][] visited = new boolean[board.length][board[0].length];
        for(int i=0;i<board.length;i++){
            for(int j=0;j<board[0].length;j++){
                char ch = board[i][j];
                if(!wordsTrie.root.children.containsKey(ch)) {
                	continue;
                }
                Position position = new Position(i,j);
                StringBuilder prefix = new StringBuilder();
                prefix.append(ch);
                TrieNode currNode = wordsTrie.root.children.get(ch);
                visited[i][j] = true;
                dfs(board, 
                    wordsTrie,
                    position, 
                    currNode, 
                    prefix, 
                    visited,
                    results);
                visited[i][j] = false;

            
            }
        }

        return results;
    }
    
    //define: find all words generated from board in trie
    private void dfs(char[][] board,
                     Trie trie,
                     Position position, 
                     TrieNode currNode,
                     StringBuilder prefix,
                     boolean[][] visited, 
                     List<String> results){

        //output:the char reaches the end of a word
        if(currNode.isEnd){
            //把已经查找到的单词isEnd=false,防止重复查找 
        	currNode.isEnd = false;
            results.add(new String(prefix));
        }
        
        
        for(int i=0;i<4;i++){
            Position nextPosition = new Position(position.x + dx[i],
                                        position.y + dy[i]
            );
            //if the char exsits in board 
            if(!isBound(board, nextPosition)){
                continue;
            }
            
            if(visited[nextPosition.x][nextPosition.y]){
                continue;
            }
            char nextCh = board[nextPosition.x][nextPosition.y];
            if(!currNode.children.containsKey(nextCh)){
                continue;
            }
            TrieNode nextNode = currNode.children.get(nextCh);
            //backtracking
            prefix.append(nextCh);
            visited[nextPosition.x][nextPosition.y] = true;
            dfs(board, trie, nextPosition, nextNode, prefix, visited, results);
            //回溯后状态复原
            prefix.deleteCharAt(prefix.length() - 1);
            visited[nextPosition.x][nextPosition.y] = false;
        }
        
    }
    
    boolean isBound(char[][] board, Position pos){
        if(pos.x < 0 || pos.x >= board.length || pos.y < 0 || pos.y >= board[0].length){
            return false;
        }
        return true;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值