【字典树】添加和查找单词

Add and Search Word - Data structure design

问题:

Design a data structure that supports the following two operations:

void addWord(word)
bool search(word)

search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter.

For example:

addWord("bad")
addWord("dad")
addWord("mad")
search("pad") -> false
search("bad") -> true
search(".ad") -> true
search("b..") -> true

Note:
You may assume that all words are consist of lowercase letters a-z.

 hint:

You should be familiar with how a Trie works. If not, please work on this problem: Implement Trie (Prefix Tree) first.

解决:

① 要求实现添加和查找单词,实际上是实现向字典树种添加词组,并可以查找匹配词组。与 Implement Trie (Prefix Tree) 相似,这道题里面'.'可以代替任意字符,所以一旦有了'.',就需要查找所有的子树,只要有一个返回true,整个search函数就返回true。

class TrieNode{//222ms
    char c;//当前节点的字符
    Map<Character,TrieNode> children = new HashMap<>();//当前节点的子节点,以当前节点的字符作为key
    boolean isLeaf;
    public TrieNode(){}
    public TrieNode(char c){
        this.c = c;
    }
}
public class WordDictionary{
    private TrieNode root;//指定根节点,没有字符。
    public WordDictionary(){
        root = new TrieNode();//根节点没有字符
    }
    public void addWord(String word){
        Map<Character,TrieNode> cur = root.children;//从根节点的子节点开始向下添加
        for (int i = 0;i < word.length() ;i ++ ) {
            char c = word.charAt(i);//当前要插入的字符
            TrieNode tmp = null;//对应的节点
            if (cur.containsKey(c)) {//当前字符对应的节点已经存在了
                tmp = cur.get(c);//获取遍历到的节点
            }else{
                tmp = new TrieNode(c);
                cur.put(c,tmp);
            }
            cur = tmp.children;
            if (i == word.length() - 1) {
                tmp.isLeaf = true;
            }
        }
    }
    public boolean search(String word){
        return dfsSearch(root.children,word,0);
    }
    public boolean dfsSearch(Map<Character,TrieNode> cur,String word,int i){
        if (i == word.length()) {
            if (cur.size() == 0) {//节点与单词都遍历完了
                return true;
            }else{
                return false;
            }
        }
        char c = word.charAt(i);
        if (cur.containsKey(c)) {
            if (i == word.length() - 1 && cur.get(c).isLeaf) {
                return true;
            }
            return dfsSearch(cur.get(c).children,word,i + 1);
        }else if(c == '.'){
            boolean res = false;
            for (Map.Entry<Character,TrieNode> child : cur.entrySet()) {
                if (i == word.length() - 1 && child.getValue().isLeaf) {
                    return true;
                }
                if(dfsSearch(child.getValue().children,word,i + 1)){
                    res = true;
                }
            }
            return res;
        }else{
            return false;
        }
    }
}

② 使用数组代替Map。

class TrieNode{//186ms
    TrieNode[] children;
    boolean isLeaf;
    public TrieNode(){//单词中全部为26个小写字母。
        children = new TrieNode[26];
    }
}
public class WordDictionary {
    TrieNode root;
    public WordDictionary(){
        root = new TrieNode();//根节点为空
    }
    public void addWord(String word){
        TrieNode cur = root;
        for (int i = 0;i < word.length() ;i ++ ) {//构造前缀树
            char c = word.charAt(i);
            int index = c - 'a';//在孩子节点中的位置
            if (cur.children[index] == null) {
                TrieNode tmp = new TrieNode();
                cur.children[index] = tmp;
                cur = tmp;
            }else{
                cur = cur.children[index];
            }
        }
        cur.isLeaf = true;
    }
    public boolean search(String word){
        return dfsSearch(root,word,0);
    }
    public boolean dfsSearch(TrieNode cur,String word,int i){
        if (i == word.length() && cur.isLeaf) {
            return true;
        }
        if (i >= word.length()) {
            return false;
        }
        char c = word.charAt(i);
        if (c == '.') {
            boolean res = false;
            for (int j = 0;j < 26 ;j ++ ) {//查看是否有匹配的子节点
                if (cur.children[j] != null) {
                    if (dfsSearch(cur.children[j],word,i + 1)) {
                        res = true;
                        break;
                    }
                }
            }
            if (res) {
                return true;
            }
        }else{
            int index = c - 'a';
            if (cur.children[index] != null) {
                return dfsSearch(cur.children[index],word,i + 1);
            }else{
                return false;
            }
        }
        return false;
    }
}

③ 在discuss中看到的,使用map保存节点及其对应路径上的长度,值为字符串的长度,键为对应的字符串的长度。

public class WordDictionary{//160ms
    //使用map构造前缀树,使用链表保存节点值.键为字符串长度,值为对应的字符串
    Map<Integer,List<String>> map = new HashMap<>();
    public void addWord(String word){
        int index = word.length();
        if (! map.containsKey(index)) {
            List<String> list = new ArrayList<>();
            list.add(word);
            map.put(index,list); 
        }else{
            map.get(index).add(word);
        }
    }
    public boolean search(String word){
        int index = word.length();
        if(! map.containsKey(index)){
            return false;
        }
        List<String> list = map.get(index);
        for (String s : list) {
            if (isSame(s,word)) {
                return true;
            }
        }
        return false;
    }
    public boolean isSame(String root,String search){
        if (root.length() != search.length()) {
            return false;
        }
        for (int i = 0;i < root.length() ;i ++ ) {
            if (search.charAt(i) != '.' && search.charAt(i) != root.charAt(i)) {
                return false;
            }
        }
        return true;
    }
}

转载于:https://my.oschina.net/liyurong/blog/1575284

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值