1. 程式人生 > 其它 >leetcode-211. 新增與搜尋單詞 - 資料結構設計

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;
        };
    };
};