leetcode-211. 添加与搜索单词

Posted 流光之中

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode-211. 添加与搜索单词相关的知识,希望对你有一定的参考价值。

leetcode-211. 添加与搜索单词 - 数据结构设计

题目:

请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。

实现词典类 WordDictionary :

  • WordDictionary() 初始化词典对象
  • void addWord(word) 将 word 添加到数据结构中,之后可以对它进行匹配
  • bool search(word) 如果数据结构中存在字符串与 word 匹配,则返回 true ;否则,返回 false 。word 中可能包含一些 \'.\' ,每个 . 都可以表示任何一个字母。

思路:

  • 本题直接将所有的单词存入数组中,在查找时遍历所有单词,但这样代码查找效率低下,可能会超时,因此我们可以对单词根据代码根据长度进行分类,存储到map<int,vector<string>>的数据结构中,map的key为长度,value为对应长度的单词,插入单词时先计算单词长度再插入对应的数组中,查找时,直接根据单词长度找到对应的集合,遍历集合中的所有单词,再进行逐字符匹配,如果是\'.\'则跳过该次,如果有单词匹配成功则返回true。

  • 本题也可以通过前缀树解决,因为单词只有26个,因此构建的字典树为26叉树,并置一个标志isEnd记录此字母是否为单词末尾

    数据结构:

    class TrieNode{
        public:
        //存储孩子节点
        vector<TrieNode*> child;
        //末尾标志
        bool isEnd;
        TrieNode(){
            //初始化孩子节点
            child=vector<TrieNode*>{26,nullptr};
            //默认置为false,插入时将最后一个字母的标志置为true
            isEnd=false;
        };
    };
    

    插入元素时,通过遍历将单词合并到前缀树

    查找元素时,

    • 如果遍历到单词末尾,则意味着单词匹配成功,返回true
    • 如果单当前字母为a-z,则递归判断trie->child[word[i]-\'a\']和word[i+1:]的查询结果
    • 如果遍历的当前字母为\'.\',则需要对每个子节点都进行一次递归查找

Code

  • map集合
class WordDictionary {
public:
    map<int,vector<string>> m;
    WordDictionary() {
        
    }
    
    void addWord(string word) {
        if(m.count(word.size())==0){
            vector<string> cur={word};
            m[word.size()]=cur;
        }
        else{
            m[word.size()].push_back(word);
        }
    }
    
    bool search(string word) {
        int len=word.size();
        if(m.count(len)==0){
            return false;
        }
        for(auto item:m[word.size()]){
            bool have=true;
            for(int i=0;i<len;i++){
                if(word[i]!=\'.\' && word[i]!=item[i]){
                    have=false;
                }
            }
            if(have){
                return true;
            }
        }
        return false;
    }
};
  • 前缀树
class TrieNode{
    public:
    vector<TrieNode*> child;
    bool isEnd;
    TrieNode(){
        child=vector<TrieNode*>{26,nullptr};
        isEnd=false;
    };
};
class WordDictionary {
public:
    TrieNode* root;
    WordDictionary() {
        root=new TrieNode();
    }
    
    void addWord(string word) {
        TrieNode* cur=root;
        for(int i=0;i<word.size();i++){
            if(cur->child[word[i]-\'a\']==nullptr){
                cur->child[word[i]-\'a\']=new TrieNode();
            }
            cur=cur->child[word[i]-\'a\'];
        }
        //末尾标志置位true
        cur->isEnd=true;
    };
    
    bool search(string word) {
        TrieNode* cur=root;
        //递归查询
        return dfs(cur,0,word);
    };
    bool dfs(TrieNode* root,int inx,string word){
        //查找到末尾
        if(inx==word.size()){
            return root->isEnd;
        };
        //遇到\'.\'需要将26个字母都遍历一次
        if(word[inx]==\'.\'){
            for(int i=0;i<26;i++){
                if(root->child[i]!=nullptr && dfs(root->child[i],inx+1,word)){
                    return true;
                }
            }
            return false;
        }
        else{
            if(root->child[word[inx]-\'a\']!=nullptr && dfs(root->child[word[inx]-\'a\'],inx+1,word)){
                return true;
            }
            return false;
        };
    };
};

LeetCode 211.添加与搜索单词

请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。

实现词典类 WordDictionary :

WordDictionary() 初始化词典对象
void addWord(word) 将 word 添加到数据结构中,之后可以对它进行匹配
bool search(word) 如果数据结构中存在字符串与 word 匹配,则返回 true ;否则,返回  false 。word 中可能包含一些 '.' ,每个 . 都可以表示任何一个字母。

1 <= word.length <= 25
addWord 中的 word 由小写英文字母组成
search 中的 word 由 '.' 或小写英文字母组成
最多调用 10^4 次 addWord 和 search


WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("bad");
wordDictionary.addWord("dad");
wordDictionary.addWord("mad");
wordDictionary.search("pad"); // 返回 False
wordDictionary.search("bad"); // 返回 True
wordDictionary.search(".ad"); // 返回 True
wordDictionary.search("b.."); // 返回 True

211. 添加与搜索单词


犹豫不决, 没有思路的时候就先想想暴力法, 

  1. addWord的时候把单词加入到一个集合中,
  2. 到搜索的时候, 如果入参的单词含有 "." , 那就把这个 "."  换成 "a ~ z"的所有字母, 枚举出所有可能的组合,
    1. 如果这个组合中有和集合中重复的元素, 那就说明存在,  返回ture
    2. 如果集合中和枚举出的所有情况都没有交集, 说明不存在, 返回false

这个是2种暴力法的实现, 时间复杂度取决于点的数量, 如果有n个 ".", 那就是O(26^n),  到LeetCode上运行 果然出现了超时, 

// 暴力法1
class WordDictionary 
    var dictionarySet = Set<String>.init()
    init() 
    
    func addWord(_ word: String) 
        dictionarySet.update(with: word)
    
    func search(_ word: String) -> Bool 
        // 暴力法,把work所有的情况枚举出来放到一个集合中
        let wordSet = changeWorkToSet(word: word)
        // 取2个集合的并集, 结果为空, 说明不包含; 结果有值, 说明存在
        let resultSet = dictionarySet.intersection(wordSet)
        if resultSet.isEmpty 
            return false
         else 
            return true
        
    

    func changeWorkToSet(word : String) -> Set<String> 
        // 包含点,把一个点换成具体的字符,然后递归调用自己,直到所有的.都消失
        if word.contains(".") 
            var set = Set<String>.init()
            let startIndex = word.firstIndex(of: ".")!
            let endIndex = word.index(startIndex, offsetBy: 1)
            let alphabet: [String] = ["a", "b", "c", "d", "e", "f",
                                      "g", "h", "i", "j", "k", "l",
                                      "m", "n", "o", "p", "q", "r",
                                      "s", "t", "u", "v", "w", "x",
                                      "y", "z"]
            for replaceChar in alphabet 
                var newWord = word
                newWord.replaceSubrange(startIndex..<endIndex, with: replaceChar)
                let resultSet = changeWorkToSet(word: newWord)
                for resultWord in resultSet 
                    set.update(with: resultWord)
                
            
            return set
         else 
            // 不包含点, 直接返回一个set,set中只有一个元素
            return Set.init(arrayLiteral: word)
        
    


// 暴力法2, 计算出一个结果就会调, 没有必要把所有情况都枚举出来
class WordDictionary 
    var dictionarySet = Set<String>.init()
    // 应不应该继续遍历, 每一个新的查询开始的时候设置为true
    var shouldContinue = true
    init() 
    func addWord(_ word: String) 
        dictionarySet.update(with: word)
    

    func search(_ word: String) -> Bool 

        shouldContinue = true
        var result = false
        // 暴力法,把work所有的情况枚举出来进行回调判断
        changeWorkToComplete(word: word)  resultString in
            if self.dictionarySet.contains(resultString) 
                result = true
                self.shouldContinue = false
            
        
        return result
    


    func changeWorkToComplete(word : String, complete: @escaping (String) -> Void) 
        // 只要找到一个,结束遍历
        if !shouldContinue 
            return
        

        // 包含点,把一个点换成具体的字符,然后递归调用自己,直到所有的.都消失
        if word.contains(".") 
            let startIndex = word.firstIndex(of: ".")!
            let endIndex = word.index(startIndex, offsetBy: 1)
            let alphabet: [String] = ["a", "b", "c", "d", "e", "f",
                                      "g", "h", "i", "j", "k", "l",
                                      "m", "n", "o", "p", "q", "r",
                                      "s", "t", "u", "v", "w", "x",
                                      "y", "z"]
            for replaceChar in alphabet 
                var newWord = word
                newWord.replaceSubrange(startIndex..<endIndex, with: replaceChar)
                changeWorkToComplete(word: newWord, complete: complete)
            

         else 
            // 不包含点了, 直接回调
            complete(word)
        
    

以上是关于leetcode-211. 添加与搜索单词的主要内容,如果未能解决你的问题,请参考以下文章

leetcode-211-添加与搜索单词-数据结构设计

LeetCode 211.添加与搜索单词, swift

leetcode 211. 添加与搜索单词 - 数据结构设计 解题报告

LeetCode211. 添加与搜索单词 - 数据结构设计(相关话题:Trie前缀树)

LeetCode211. 添加与搜索单词 - 数据结构设计(相关话题:Trie前缀树)

[leetcode]211. Add and Search Word - Data structure design添加查找单词 - 数据结构设计