int trie[26][MX] {}; int cnt[MX] {}; int tot {}; bool isEnd[MX] {};
intf(char ch){ return ch - 'a'; }
voidinsert(const string& s){ int p = 0; for (char ch : s) { int o = f(ch); if (!trie[o][p]) { trie[o][p] = ++tot; } p = trie[o][p]; cnt[p]++; } isEnd[p] = true; }
boolfind(const string& s){ int p = 0; for (char ch : s) { int o = f(ch); if (!trie[o][p]) { returnfalse; } p = trie[o][p]; } return isEnd[p]; }
boolstartsWith(const string& s){ int p = 0; for (char ch : s) { int o = f(ch); if (!trie[o][p]) { returnfalse; } p = trie[o][p]; } returntrue; }
// 以 s(非空)为前缀的模式串的个数 intgetCnt(const string& s){ int p = 0; for (char ch : s) { int o = f(ch); if (!trie[o][p]) { return0; } p = trie[o][p]; } return cnt[p]; }
voidinsert(i64 x){ int p = 0; trie[p].cnt++; trie[p].mn = min(trie[p].mn, x); for (int i = N - 1; i >= 0; i--) { int o = x >> i & 1; if (trie[p].son[o] == -1) { trie[p].son[o] = newNode(); } p = trie[p].son[o]; trie[p].cnt++; trie[p].mn = min(trie[p].mn, x); } }
// ! remove 中没有维护 mn voidremove(i64 x){ int p = 0; trie[p].cnt--; for (int i = N - 1; i >= 0; i--) { int o = x >> i & 1; if (trie[p].son[o] == -1) { trie[p].son[o] = newNode(); } p = trie[p].son[o]; trie[p].cnt--; } }
// 也可以用哈希表 i64 maxXor(i64 x)const{ if (trie.size() == 1) { return -inf; } i64 ans = 0; int p = 0; for (int i = N - 1; i >= 0; i--) { int o = x >> i & 1; if (trie[p].son[o ^ 1] != -1 && trie[trie[p].son[o ^ 1]].cnt > 0) { ans |= 1ll << i; o ^= 1; } p = trie[p].son[o]; } return ans; }
i64 minXor(i64 x)const{ if (trie.size() == 1) { return inf; } i64 ans = 0; int p = 0; for (int i = N - 1; i >= 0; i--) { int o = x >> i & 1; if (trie[p].son[o] == -1 || trie[trie[p].son[o]].cnt <= 0) { ans |= 1ll << i; o ^= 1; } p = trie[p].son[o]; } return ans; }
// 返回 x 与 trie 上所有数的第 k 大异或值 // k 从 1 开始,超过总数则返回 0 i64 kth_maxXor(i64 x, int k)const{ i64 ans = 0; int p = 0; for (int i = N - 1; i >= 0; i--) { int o = x >> i & 1; int cnt = (trie[p].son[o ^ 1] == -1 ? 0 : trie[trie[p].son[o ^ 1]].cnt); if (k <= cnt) { ans |= 1ll << i; o ^= 1; } else { k -= cnt; } p = trie[p].son[o]; if (p == -1) { break; } } return ans; }
// 统计与 x 异或值 ≤ limit 的元素个数 // 也可以用哈希表 i64 countXorWithLimit(i64 x, i64 limit)const{ // 核心原理是,当 limit+1 的某一位是 1 的时候,若该位异或值取 0,则后面的位是可以取任意数字的 // 如果在 limit 上而不是 limit+1 上讨论,就要单独处理走到叶子的情况了(恰好等于 limit) limit++; i64 ans = 0; int p = 0; for (int i = N - 1; i >= 0 && p != -1; i--) { int o = x >> i & 1; if (limit >> i & 1) { if (trie[p].son[o] != -1) { ans += trie[trie[p].son[o]].cnt; } o ^= 1; } p = trie[p].son[o]; } return ans; }
// x 与 trie 上所有 ≤ limit 的数的最大异或值 // 不存在时返回 -1 i64 maxXorWithLimit(i64 x, i64 limit)const{ if (trie[0].mn > limit) { return-1; } i64 ans = 0; int p = 0; for (int i = N - 1; i >= 0; --i) { int o = (x >> i) & 1; if (trie[p].son[o ^ 1] != -1 && trie[trie[p].son[o ^ 1]].cnt > 0 && trie[trie[p].son[o ^ 1]].mn <= limit) { ans |= 1ll << i; o ^= 1; } p = trie[p].son[o]; } return ans; }
// x 与 trie 上所有数的异或 <= limit 的最大异或值 // 不存在时返回 -1 i64 maxXorWithLimitXor(i64 x, i64 limit)const{ limit++; i64 ans = 0; int p = 0; // 记录最后一次我们“仍能走 0 分支”,但 limit 那位是 1 的情况 int last_p = -1, last_i = -1; i64 last_ans = 0; for (int i = N - 1; i >= 0 && p != -1; i--) { int o = x >> i & 1; if (limit >> i & 1) { // 分两种:如果走 son[o],当前位异或 0,依然 < limit 在这位的 1 if (trie[p].son[o] != -1) { last_p = trie[p].son[o]; last_i = i - 1; last_ans = ans; } // 如果走 son[o^1],当前位异或 1 if (trie[p].son[o ^ 1] != -1) { ans |= 1ll << i; } // 实际走的还是 o^1 o ^= 1; } p = trie[p].son[o]; } if (last_p == -1) { return-1; } ans = last_ans; p = last_p; for (int i = last_i; i >= 0; i--) { int o = x >> i & 1; if (trie[p].son[o ^ 1] != -1 && trie[trie[p].son[o ^ 1]].cnt > 0) { ans |= 1ll << i; o ^= 1; } p = trie[p].son[o]; } return ans; }
// 完全图,边权为 a[u]^a[v],求 MST // Boruvka 算法,分治连边 template <integral T> autoxorMST(const vector<T>& a){ T ans {}; auto dfs = [&](auto&& dfs, auto& a, int p) { if (a.empty() || p < 0) { return; } vector<T> b[2]; b[0].reserve(a.size()); b[1].reserve(a.size()); for (auto& v : a) { b[v >> p & 1].push_back(v); } if (!b[0].empty() && !b[1].empty()) { if (b[0].size() > b[1].size()) { swap(b[0], b[1]); } Trie t(30); // todo t.reserve(b[0].size()); for (auto& x : b[0]) { t.insert(x); } T minXor = numeric_limits<T>::max(); for (auto& x : b[1]) { minXor = min(minXor, t.minXor(x)); } ans += minXor; } dfs(dfs, b[0], p - 1); dfs(dfs, b[1], p - 1); }; dfs(dfs, a, N - 1); return ans; } };
/** * @brief Trie class, implementation of trie using hashmap in each trie node * for all the characters of char16_t(UTF-16)type with methods to insert, * delete, search, start with and to recommend words based on a given * prefix. */ classTrie { private: structNode { // unordered map with key type char16_t and value is a shared pointer type of Node std::unordered_map<char16_t, std::shared_ptr<Node>> children; // boolean variable to represent the node end bool word_end = false; };
// declaring root node of trie std::shared_ptr<Node> root_node = std::make_shared<Node>();
voiddelete_word(std::string word){ std::shared_ptr<Node> curr = root_node; std::stack<std::shared_ptr<Node>> nodes; int cnt = 0; for (char ch : word) { if (curr->children.find(ch) == curr->children.end()) { return; } if (curr->word_end) { cnt++; }
nodes.push(curr->children[ch]); curr = curr->children[ch]; } // Delete only when it's a word, and it has children after // or prefix in the line if (nodes.top()->word_end) { nodes.top()->word_end = false; } // Delete only when it has no children after // and also no prefix in the line while (!(nodes.top()->word_end) && nodes.top()->children.empty()) { nodes.pop(); nodes.top()->children.erase(word.back()); word.pop_back(); } }
/** * @brief helper function to predict/recommend words that starts with a * given prefix from the end of prefix's node iterate through all the child * nodes by recursively appending all the possible words below the trie * @param prefix string to recommend the words * @param element node at the end of the given prefix * @param results list to store the all possible words * @returns list of recommended words */ std::vector<std::string> get_all_words(std::vector<std::string> results, const std::shared_ptr<Node>& element, std::string prefix){ if (element->word_end) { results.push_back(prefix); } if (element->children.empty()) { return results; } for (autoconst& x : element->children) { std::string key = ""; key = x.first; prefix += key; results = get_all_words(results, element->children[x.first], prefix); prefix.pop_back(); } return results; }
// predict/recommend a word that starts with a given prefix and return a list of recommended words std::vector<std::string> predict_words(const std::string& prefix){ std::vector<std::string> result; std::shared_ptr<Node> curr = root_node; // traversing until the end of the given prefix in trie for (char ch : prefix) { if (curr->children.find(ch) == curr->children.end()) { return result; } curr = curr->children[ch]; } // if the given prefix is the only word without children if (curr->word_end && curr->children.empty()) { result.push_back(prefix); return result; } // iteratively and recursively get the recommended words returnget_all_words(result, curr, prefix); } };