// 字典树 学习笔记 // 字符串 Trie 具体实现 int trie[Maxn][26], v[Maxn], tot; // trie 数组记录边的信息, v 记录点的信息, tot 为总结点数 voidinsert(char* s){ int p = 0, n = strlen(s); for (int k = 0; k < n; ++k) { int ch = s[k] - 'a'; // 将字符转为 int if (!trie[p][ch]) trie[p][ch] = ++tot; // 如果没有就新建节点 p = trie[p][ch]; // 直接跳 } v[p]++; // 标记跳到这里有一个字符串结束了 }
intsearch(char* s){ int n = strlen(s), p = 0; for (int k = 0; k < n; ++k) { p = trie[p][s[k] - 'a']; // 直接跳 if (!p) returnfalse; // 节点不存在 } return v[p]; // 已经跳完所有边 }
// 字典树 学习笔记 // 01Trie 具体实现 voidinsert(int val){ int x = 0; bool c; for (int k = (1 << 30); k; k >>= 1) { //最高位补齐 c = val & k; if (!trie[x][c]) { //如果没有就新建节点 trie[x][c] = ++tot; } x = trie[x][c]; //跳到子节点 } }
intask(int val){ int ans = 0, x = 0; bool c; for (int k = (1 << 30); k; k >>= 1) { c = val & k; if (trie[x][!c]) { //如果存在相反的方向就向相反的方向跳,同时计入答案 ans += k; x = trie[x][!c]; //这里直接就入了异或和 } else x = trie[x][c]; //否则就向相同的方向跳 } return ans; }
// 字典树 学习笔记 // Trie 树实现平衡树功能 || 插入修改操作具体实现 voidinsert(int val, int k){ int p = 1; for (int i = Maxk - 1; i >= 0; --i) { int c = (val >> i) & 1; if (!trie[p][c]) trie[p][c] = ++cnt; p = trie[p][c]; v[p] += k; } }
查询排名操作
具体实现:一位一位的匹配,如果当前位为 0 直接向左跳,否则将左半边计入答案,向右跳。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 字典树 学习笔记 // Trie 树实现平衡树功能 || 查询排名操作具体实现 intcheck_nlt(int x){ int p = 1, ans = 0; for (int i = Maxk - 1; i >= 0; i--) { if ((x >> i) & 1) { ans += v[trie[p][0]]; p = trie[p][1]; } else p = trie[p][0]; if (!p) break; } return ans; } //直接查到的是比 x 小的数的个数,排名要 +1
根据排名查询数值操作
具体实现:类似线段树二分,如果左边个数小于 k 直接向左跳,否则 查询右半边 排名为 k-左半边个数 的数。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 字典树 学习笔记 // Trie 树实现平衡树功能 || 根据排名查询数值具体实现 intcheck_kth(int k){ int p = 1, ans = 0; for (int i = Maxk - 1; i >= 0; --i) { if (v[trie[p][0]] >= k) p = trie[p][0]; else { ans |= (1 << i); k -= v[trie[p][0]]; p = trie[p][1]; } } return ans; }