作用:Trie是一种用于元素范围较小(如0/1,26个字母),常用于字符串前缀、异或值相关的
原理:前缀树,每个节点有固定的sigma个节点,同一层是元素们的同一pos。
实现:
非动态开点:
leetcode 1707. 与数组中元素的最大异或值
思路:先排序,再只把小于等于limit的加入,再求该元素与数组元素的最大值。
class Solution { public: #define maxnode 32*100000+10 #define sigma 2 struct Trie { int ch[maxnode][sigma]; int value[maxnode]; // 叶子节点的值 int cnt[maxnode]; // 经该节点的元素个数 int sz = 1; void init(){ memset(ch, 0 ,sizeof(ch)); memset(cnt, 0, sizeof(cnt)); memset(value, 0, sizeof(value)); } void insert(int x){ int u = 0; for(int i = 31; i >= 0; i--){ int c = (x>>i)&1; if(!ch[u][c]) ch[u][c] = sz++; u = ch[u][c]; cnt[u]++; } value[u] = x; } // 查询与x异或的最大值 int query(int x){ int u = 0, res = 0; for(int i = 31; i >= 0; --i){ int a = (x>>i)&1; // cout << a << " " << b << endl; if(ch[u][a^1]) u = ch[u][a^1]; else u = ch[u][a]; } return value[u]^x; } }trie; struct Node { int x, limit, id; Node(int x, int limit, int id) : x(x), limit(limit), id(id) {} bool operator < (const Node& node) { return this->limit < node.limit; } }; vector<int> maximizeXor(vector<int>& nums, vector<vector<int>>& queries) { sort(nums.begin(), nums.end()); vector<Node>myqueries; for(int i = 0;i < queries.size();i++) { myqueries.push_back(Node(queries[i][0], queries[i][1], i)); } sort(myqueries.begin(), myqueries.end()); trie.init(); vector<int>res(queries.size()); int pos = 0; // nums数组的当前位置 for(auto query : myqueries) { int x = query.x, limit = query.limit, id = query.id; // cout << x << " " << limit << " " << id << endl; while(pos < nums.size() && nums[pos] <= limit) trie.insert(nums[pos++]); // cout << "pos: " << pos << endl; if(pos == 0) res[id] = -1; else res[id] = trie.query(x); } return res; } };
动态开点:
leetcode211. 添加与搜索单词 - 数据结构设计
思路:动态开点,用节点指针。遇到 通配符‘.’ 进行dfs
class WordDictionary { public: /** Initialize your data structure here. */ struct Node { Node* son[26]; bool is_end; Node(){ for(int i = 0;i < 26;i++) son[i] = NULL; is_end = false; } }; // static Node* root; struct Trie { Node* root; void init(){ root = new Node(); } void insert(string str){ Node* p = root; for(char mych : str){ int c = mych-'a'; if(!p->son[c]) p->son[c] = new Node(); p = p->son[c]; } // value[u] = str; p->is_end = true; } // 查询str是否存在 bool query(string str, int pos, Node* p){ // cout << str << " " << pos << " " << u << endl; if(pos == str.size()) return p->is_end; char mych = str[pos]; if(mych == '.') { for(int i = 0;i < 26;i++) { if(p->son[i] && query(str, pos+1, p->son[i])) return true; } return false; } else { int c = mych-'a'; if(!p->son[c]) return false; else { return query(str, pos+1, p->son[c]); } } } }trie; WordDictionary() { trie.init(); } void addWord(string word) { trie.insert(word); } bool search(string word) { return trie.query(word, 0, trie.root); } };
其他的一些例子:
leetcode 745. 前缀和后缀搜索
思路:插入的时候加trick,并给每个节点更新权重,查询还是普通查询
class WordFilter { public: struct Node { Node* son[27]; int weight; Node(){ for(int i = 0;i < 27;i++) son[i] = NULL; weight = -1; } }; // static Node* root; struct Trie { Node* root; void init(){ root = new Node(); } void insert(string str, int index){ Node* p = root; for(char mych : str){ int c; if(mych == '#') c = 26; else c = mych-'a'; if(!p->son[c]) p->son[c] = new Node(); p = p->son[c]; p->weight = max(index, p->weight); // 每个节点都要个更新 } // value[u] = str; } // 查询str子串的权重 int query(string str){ int n = str.size(); Node* p = root; for(int i = 0;i < n;i++) { int c; if(str[i] == '#') c = 26; else c = str[i]-'a'; if(!p->son[c]) return -1; p = p->son[c]; } return p->weight; } }trie; WordFilter(vector<string>& words) { trie.init(); for(int i = 0;i < words.size();i++) { string suf = ""; string word = words[i]; int n = word.size(); for(int j = 0;j <=n;j++) { // cout << suf+'#'+word << endl; trie.insert(suf+'#'+word, i); if(j!=n) suf = word[n-1-j] + suf; } } } int f(string prefix, string suffix) { string word = suffix + '#' + prefix; return trie.query(word); } }; /** * Your WordFilter object will be instantiated and called as such: * WordFilter* obj = new WordFilter(words); * int param_1 = obj->f(prefix,suffix); */
leetcode 1032. 字符流
题意:最新查询的k的字符,是否能组成字典中的某个单词
思路:按后缀建树并查询,主要string类型的letter拼接会超时,要用char数组来做。
class StreamChecker { public: struct Node { Node* son[26]; bool is_end; Node() { for(int i = 0;i < 26;i++) son[i] = NULL; is_end = false; } }; struct Trie { Node* root = new Node(); void insert(string str) { Node* p = root; for(char ch : str) { int c = ch-'a'; if(!p->son[c]) p->son[c] = new Node(); p = p->son[c]; } p->is_end = true; } bool query(char* myletter, int cnt) { Node* p = root; for(int i = cnt-1;i >= 0;i--) { int c = myletter[i]-'a'; if(!p->son[c]) break; p = p->son[c]; if(p->is_end) return true; // 过程中有,也直接返回 } return p->is_end; } }trie; // string myletter = ""; char myletter[40010]; int cnt = 0; StreamChecker(vector<string>& words) { for(string word : words) { reverse(word.begin(), word.end()); trie.insert(word); } for(int i = 0;i < 40010;i++) myletter[i] = '