• Trie


    参考

    https://en.wikipedia.org/wiki/Trie

    a trie, also called digital tree or prefix tree, is a kind of search tree—an ordered tree data structure used to store a dynamic set or associative array where the keys are usually strings

    也叫前缀树, 特定情况下替代set 和map。这时候key是字符串。


    image

    LeetCode:208. Implement Trie (Prefix Tree)
    LeetCode:211. Add and Search Word - Data structure design
    LeetCode:212. Word Search II


    下面我们就一步步来实现Trie,并做题

    一、初步实现Trie树结构


    208. Implement Trie (Prefix Tree)

    https://leetcode.com/problems/implement-trie-prefix-tree/description/


    非递归实现


    #include<cstdio>

    #include<iostream>
    #include<string>
    #include<vector>
    #include<algorithm>
    using namespace std;


    /**
     * Your Trie object will be instantiated and called as such:
     * Trie* obj = new Trie();
     * obj->insert(word);
     * bool param_2 = obj->search(word);
     * bool param_3 = obj->startsWith(prefix);
     */

    const int letter_size=26;//字符集大小
    struct TrieNode
    {
        TrieNode* children[letter_size]={0};
        bool isWord = false;
        TrieNode(){
        }
        ~TrieNode(){
            for(int i=0; i<letter_size; i++) if(children[i]) delete children[i];
        }
    };

    struct Trie {
        TrieNode* root;

        /** Initialize your data structure here. */
        Trie() {
            root=new TrieNode();
        }

        ~Trie(){
            delete root;
        }

        int idx(char c)
        {
            return c - 'a';
        }

        /** Inserts a word into the trie. */
        void insert(string word) {
            TrieNode*p = root;
            int len = word.length();
            for(int i=0; i < len; i++){
                int c = idx(word[i]);
                if(!p->children[c])
                    p->children[c] = new TrieNode();
                p = p->children[c];
            }
            p->isWord = true;
        }

        //辅助函数
        TrieNode * _search(string &word)
        {
            TrieNode *p=root;
            int len = word.length();
            for(int i=0; i < len; i++){
                TrieNode *t = p->children[idx(word[i])];
                if(!t)
                    return 0;
                else
                    p = t;
            }
            return p;
        }

        /** Returns if the word is in the trie. */
        bool search(string word) {
            TrieNode *p=_search(word);
            return p && p->isWord;
        }

        /** Returns if there is any word in the trie that starts with the given prefix. */
        bool startsWith(string word) {
            return _search(word);;
        }
    };




    int main()
    {
        Trie trie;

        trie.insert("apple");
        cout<< trie.search("apple") << endl;   // returns true
        cout<< trie.search("app") << endl;     // returns false
        cout<< trie.startsWith("app") << endl; // returns true
        trie.insert("app");
        cout<< trie.search("app") << endl;;     // returns true

        return 0;
    }


    递归实现:

    #include<cstdio>

    #include<iostream>
    #include<string>
    #include<vector>
    #include<algorithm>
    using namespace std;


    /**
     * Your Trie object will be instantiated and called as such:
     * Trie* obj = new Trie();
     * obj->insert(word);
     * bool param_2 = obj->search(word);
     * bool param_3 = obj->startsWith(prefix);
     */

    const int letter_size=26;//字符集大小
    struct TrieNode
    {
        TrieNode* children[letter_size]={0};
        bool isWord = false;
        TrieNode(){
        }
        ~TrieNode(){
            for(int i=0; i<letter_size; i++) if(children[i]) delete children[i];
        }
    };

    struct Trie {
        TrieNode* root;

        /** Initialize your data structure here. */
        Trie() {
            root=new TrieNode();
        }
        // OJ上去掉析构会快很多
        ~Trie(){
            delete root;
        }

        int idx(char c)
        {
            return c - 'a';
        }

        /** Inserts a word into the trie. */
        void insert(string word) {
            TrieNode*p = root;
            int len = word.length();
            for(int i=0; i < len; i++){
                int c = idx(word[i]);
                if(!p->children[c])
                    p->children[c] = new TrieNode();
                p = p->children[c];
            }
            p->isWord = true;
        }

        TrieNode* dfs(TrieNode *root, string &word, int pos)
        {
            if(pos==word.length())
                return root;

            int c = word[pos];
            TrieNode* p = root->children[idx(c)];
            if(p)
                return dfs(p, word, pos+1);
            else
                return 0;
        }

        /** Returns if the word is in the trie. */
        bool search(string word) {
            TrieNode *p = dfs(root, word, 0);
            return p && p->isWord;
        }

        /** Returns if there is any word in the trie that starts with the given prefix. */
        bool startsWith(string word) {
            TrieNode *p = dfs(root, word, 0);
            return p!=0;
        }
    };




    int main()
    {
        Trie trie;

        trie.insert("apple");
        cout<< trie.search("apple") << endl;   // returns true
        cout<< trie.search("app") << endl;     // returns false
        cout<< trie.startsWith("app") << endl; // returns true
        trie.insert("app");
        cout<< trie.search("app") << endl;;     // returns true

        return 0;
    }


    二、Trie树的运用:Trie树+回溯法

    充分理解这道题后,就可以接着将这种数据结构投入运用了。

    这一道题和上一题类似,需要你实现两个API,一个 插入,一个查找。插入和上一题相同,查找则需要实现“模糊查找”即:
    addWord("bad")
    addWord("dad")
    addWord("mad")
    search("pad") -> false
    search("bad") -> true
    search(".ad") -> true
    search("b..") –> true

    https://leetcode.com/problems/add-and-search-word-data-structure-design


    #include<cstdio>

    #include<iostream>
    #include<string>
    #include<vector>
    #include<algorithm>
    using namespace std;


    /**
     * Your Trie object will be instantiated and called as such:
     * Trie* obj = new Trie();
     * obj->insert(word);
     * bool param_2 = obj->search(word);
     * bool param_3 = obj->startsWith(prefix);
     */

    const int letter_size=26;//字符集大小
    struct TrieNode
    {
        TrieNode* children[letter_size]={0};
        bool isWord = false;
        TrieNode(){
        }
        ~TrieNode(){
            for(int i=0; i<letter_size; i++) if(children[i]) delete children[i];
        }
    };

    struct Trie {
        TrieNode* root;

        /** Initialize your data structure here. */
        Trie() {
            root=new TrieNode();
        }
        // OJ上去掉析构会快很多
        ~Trie(){
            delete root;
        }

        int idx(char c)
        {
            return c - 'a';
        }

        /** Inserts a word into the trie. */
        void insert(string word) {
            TrieNode*p = root;
            int len = word.length();
            for(int i=0; i < len; i++){
                int c = idx(word[i]);
                if(!p->children[c])
                    p->children[c] = new TrieNode();
                p = p->children[c];
            }
            p->isWord = true;
        }

        TrieNode* dfs(TrieNode *root, string &word, int pos)
        {
            if(pos==word.length())
                return root;

            int c = word[pos];
            // . 的话,匹配所有子节点
            if(c=='.')
            {
                for(auto p : root->children)
                {
                    if(p)
                    {
                        TrieNode* t = dfs(p, word, pos+1);
                        if(t && t->isWord) return t;//如果找到一个,直接返回(需要判断是否是word,否则可能返回中间节点)
                    }
                }
            }
            else// 否则,正常匹配
            {
                TrieNode* p = root->children[idx(c)];
                if(p)
                    return dfs(p, word, pos+1);
                else
                    return 0;
            }
            return 0;
        }

        /** Returns if the word is in the trie. */
        bool search(string word) {
            TrieNode *p = dfs(root, word, 0);
            return p && p->isWord;
        }

    //    /** Returns if there is any word in the trie that starts with the given prefix. */
    //    bool startsWith(string word) {
    //        TrieNode *p = dfs(root, word, 0);
    //        return p!=0;
    //    }
    };



    //["WordDictionary","addWord","addWord","addWord","addWord","addWord","addWord","addWord","addWord","search","search","search","search","search","search","search","search","search","search"]
    //[[],["ran"],["rune"],["runner"],["runs"],["add"],["adds"],["adder"],["addee"],["r.n"],["ru.n.e"],["add"],["add."],["adde."],[".an."],["...s"],["....e."],["......."],["..n.r"]]
    int main()
    {
        Trie trie;
        vector<string> input={"ran","rune","runner","runs","add","adds","adder","addee"};
        for(string s: input)
        {
            cout<<s<<endl;
            trie.insert(s);
        }
        vector<string> search={"r.n","ru.n.e","add","add.","adde.",".an.","...s","....e.",".......","..n.r"};
        for(string s: search)
        {
            cout<< trie.search(s) << endl;
        }

        cout<< trie.search("add.") << endl;   // returns true


        return 0;
    }

    三、Trie树的运用:Trie树+深度优先搜索

    Word Search II - LeetCode​leetcode.com

    此题咋一看,很简单嘛,把针对每个单词进行一次深度优先搜索不就行了?这个思路是可行的,但十有八九会超时。因为深度优先搜索本身就是一种比较耗时间和内存的算法。

    但这道题似乎也只好用深度优先搜索,那么,有没有可能只进行一次搜索,就查明白所有的单词是否存在于这个二维矩阵中?

    是的,这个时候你需要Trie树。而且题目也有提示,你看,output里面不是按字典顺序输出的吗?哈哈。 所以思路如下:

    1、所有单词建立Trie树

    2、dfs每个board位置, 去和Trie路径匹配。Tire当前位置有这个字母时候,才继续dfs下去。

    Example:

    Input: 
    board = [
      ['o','a','a','n'],
      ['e','t','a','e'],
      ['i','h','k','r'],
      ['i','f','l','v']
    ]
    words = ["oath","pea","eat","rain"]
    
    Output: ["eat","oath"]


    #include<cstdio>

    #include<iostream>
    #include<string>
    #include<vector>
    #include<algorithm>
    using namespace std;


    class TrieNode{//树节点结构
    public:
        TrieNode*next[26]={0};
        int isexist = -1;//-1表示节点表示的单词不存在,0及以上的数表示此单词存在且与vector<string>>words的序号对应
        TrieNode(){

        }
        ~TrieNode(){
            for(int i=0;i<26;i++){
                if(next[i])delete next[i];
            }
        }
    };

    #include <unordered_set>

    class Solution {
    public:
        TrieNode* root=0;
        int arr[4][2]={{1,0},{-1,0},{0,-1},{0,1}};//搜索的四个方向

        int idx(char c)
        {
            return c - 'a';
        }

        /** Inserts a word into the trie. */
        void insert(string word, int index) {
            TrieNode*p = root;
            int len = word.length();
            for(int i=0; i < len; i++){
                int c = idx(word[i]);
                if(!p->next[c])
                    p->next[c] = new TrieNode();
                p = p->next[c];
            }
            p->isexist = index;
        }


        void dfs(int r,int c,TrieNode*pNode,unordered_set<int>&res,vector<vector<char>>b){
            b[r][c]='0';//标记已经搜索过得地方为‘0’
            if(pNode && pNode->isexist>=0){//如果isexist>=0表示word[isexist]存在于此节点。
                res.insert(pNode->isexist);
            }
            for(int i=0;i<4;i++){//向上下左右四个方向搜索
                int newr=r+arr[i][0],newc=c+arr[i][1];
                if(newr>=0&&newc>=0&&newr<b.size()&&newc<b[0].size()&&b[newr][newc]!='0'&&pNode->next[b[newr][newc]-'a']){
                    dfs(newr,newc,pNode->next[b[newr][newc]-'a'],res,b);
                }
            }
        }

        vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
            //所有单词建立Tire
            root=new TrieNode();
            for(int i=0; i<words.size(); i++)
                insert(words[i], i);

            unordered_set<int>res;//用于保存在board中查找到的words的序号
            for(int r=0;r<(int)board.size();r++){
                for(int c=0;c<(int)board[r].size();c++){
                    if(root->next[board[r][c]-'a'])dfs(r,c,root->next[board[r][c]-'a'],res,board);
                }
            }

            vector<string>ress;//根据res中的序号制作string数组返回
            for(auto it:res){
                ress.push_back(words[it]);
            }
            return ress;
        }

    };

    int main()
    {
        vector<vector<char>>  board={
            {'o','a','a','n'},
            {'e','t','a','e'},
            {'i','h','k','r'},
            {'i','f','l','v'}
        };
        vector<string> words = {"oath","pea","eat","rain"};

        Solution s;
        vector<string> result = s.findWords(board, words);
        for(auto it:result){
            cout << it <<endl;
        }

        return 0;
    }



  • 相关阅读:
    删数问题
    八中公司_二分图带权最大匹配模板题
    完美子图(这道题太难了,得写下来要不回头又忘了)
    最近集训的图论(思路+实现)题目汇总(内容包含tarjan、分层图、拓扑、差分、奇怪的最短路):
    方格取数(简单版)+小烈送菜(不知道哪来的题)-----------奇怪的dp增加了!
    单调队列优化题:最大数(P1198)
    单调队列+线性dp题Watching Fireworks is Fun (CF372C)
    关于看了几道洛谷灰题(暂无评定)的感想
    洛谷的奇妙今日运势
    互不侵犯(洛谷P1896)
  • 原文地址:https://www.cnblogs.com/cute/p/13066819.html
Copyright © 2020-2023  润新知