数据结构 01trie树【字典树】
trie树
trie树是一种高效存储和查询字符串集合的数据结构
概念:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 【算法竞赛进阶指南】:Tire(字典树)是一种用于实现字符串快速检索的多叉树结构。Trie的每个节点都拥有若干个字符指针(是trie的第二维,记录的根节点的所有儿子节点(互为兄弟节点),若在插入或检索时扫描到一个字符c,就沿着当前节点的c字符指针,走向该指针指向的节点。 过程: 0号节点即是根节点,又是空节点。 插入: 令指针p起初指向根节点,然后依次扫描字符串S中的每个字符c 1.将字符c映射一下 2.若当前字符c不存在,则新开辟一个节点(++ idx),令p根节点的儿子节点等于idx【也就是字符c的指针指向idx】 3.让p=c的指针 查询: 令指针p起初指向根节点,然后依次扫描字符串S的每个字符c 1.将字符c映射一下 2.若p的c字符指向空,则说明S中没有被插入到trie树种,结束检索 3.若p的c字符指针指向一个已经存在的节点Q,则令p=Q。 4.当S中的字符扫描完毕时,若当前节点p被标记为一个字符串的末尾,则说明Trie中存在,否则说明S没有插入过Trie |
题目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 维护一个字符串集合,支持两种操作: I x 向集合中插入一个字符串 x; Q x 询问一个字符串在集合中出现了多少次。 共有 N 个操作,输入的字符串总长度不超过 105,字符串仅包含小写英文字母。 输入格式 第一行包含整数 N,表示操作数。 接下来 N 行,每行包含一个操作指令,指令为 I x 或 Q x 中的一种。 输出格式 对于每个询问指令 Q x,都要输出一个整数作为结果,表示 x 在集合中出现的次数。 每个结果占一行。 数据范围 1≤N≤2∗104 输入样例: 5 I abc Q abc Q ab I ab Q ab 输出样例: 1 0 1 |
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 100010; int n; char s[N], op[2]; int tr[N][26], cnt[N], idx; void insert( char s[]) { int len = strlen (s), p = 0; for ( int i = 0; i < len; i ++ ) { int t = s[i] - 'a' ; if (!tr[p][t]) tr[p][t] = ++ idx; p = tr[p][t]; } cnt[p] ++; } int query( char s[]) { int len = strlen (s), p = 0; for ( int i = 0; i < len; i ++ ) { int t = s[i] - 'a' ; if (!tr[p][t]) return 0; p = tr[p][t]; } return cnt[p]; } int main() { cin >> n; while (n -- ) { scanf ( "%s" , op); scanf ( "%s" , s); if (*op == 'I' ) { insert(s); } else { printf ( "%d\n" , query(s)); } } return 0; } |
题目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 在给定的 N 个整数 A1,A2……AN 中选出两个进行 xor(异或)运算,得到的结果最大是多少? 输入格式 第一行输入一个整数 N。 第二行输入 N 个整数 A1~AN。 输出格式 输出一个整数表示答案。 数据范围 1≤N≤105, 0≤Ai<231 输入样例: 3 1 2 3 输出样例: 3 |
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 3100010; int n; int tr[N][2], idx; //第二维记录的是根节点的儿子的兄弟节点。 void insert( int x) { // cout << x << endl; int p = 0; for ( int i = 31; i >= 0; i --) { int u = (x >> i & 1); if (!tr[p][u]) tr[p][u] = ++ idx; p = tr[p][u]; } } int query( int x) { // cout << x << endl; int p = 0, ans = 0; for ( int i = 31; i >= 0; i -- ) { int u = (x >> i & 1) ^ 1; if (tr[p][u]) { ans += 1 << i; p = tr[p][u]; } else p = tr[p][!u]; } return ans; } int main() { cin >> n; int res = 0; while (n -- ) { int x; scanf ( "%d" , &x); insert(x); res = max(res, query(x)); } cout << res << endl; return 0; } |
题目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 农夫约翰在给他的奶牛们喂食时遇到了一个问题。 他共有 N 头奶牛,编号 1∼N。 每次喂食前,这 N 头奶牛会按照 1∼N 的顺序站成一排。 此外,每头奶牛都被分配了一个可能不唯一的整数。 那么所有被分配的整数就形成了一个长度为 N 的整数序列。 请你在该整数序列中找出一个连续的非空子序列,使得子序列中元素的异或和能够最大。 如果存在多个这样的序列,那么选择序列末端整数对应的奶牛编号更小的那个序列。 如果仍然存在多个可选的序列,那么选择长度最短的那个序列。 输入格式 第一行包含整数 N。 第 2∼N+1 行,每行包含一个整数,其中第 i 行的整数表示编号为 i−1 的牛被分配的整数值。 输出格式 输出三个整数,分别表示最大的异或和,所选序列首端整数对应的奶牛编号,所选序列末端整数对应的奶牛编号。 数据范围 1≤N≤105, 分配给奶牛的整数的范围是 [0,221−1]。 输入样例: 5 1 0 5 4 2 输出样例: 6 4 5 |
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 100010; int n; int s[N]; int tr[N * 21][2], id[N * 21], idx; void insert( int x, int k) { int p = 0; for ( int i = 21; i >= 0; i -- ) { int u = x >> i & 1; if (!tr[p][u]) tr[p][u] = ++ idx; p = tr[p][u]; } id[p] = k; } int query( int x) { int p = 0; for ( int i = 21; i >= 0; i -- ) { int u = x >> i & 1; if (tr[p][!u]) p = tr[p][!u]; else p = tr[p][u]; } return id[p]; } int main() { cin >> n; for ( int i = 1; i <= n ;i ++ ) { scanf ( "%d" , &s[i]); s[i] ^= s[i - 1]; } int ans = -1, l = 1, r = 1; for ( int i = 1; i <= n; i ++ ) { // insert(s[i], i); int k = query(s[i]); int res = s[i] ^ s[k]; if (ans < res) { ans = res; l = k + 1; //如果存在s[L1 ~ R] == s[L2 ~ R],且L1<L2,在insert的时候会被覆盖掉的,所以不用考虑第三个情况,会自动保留最短 r = i; } insert(s[i], i); } cout << ans << ' ' << l << ' ' << r << endl; return 0; } |
题目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 给定一个非负整数数列 a,初始长度为 N。 请在所有长度不超过 M 的连续子数组中,找出子数组异或和的最大值。 子数组的异或和即为子数组中所有元素按位异或得到的结果。 注意:子数组可以为空。 输入格式 第一行包含两个整数 N,M。 第二行包含 N 个整数,其中第 i 个为 ai。 输出格式 输出可以得到的子数组异或和的最大值。 数据范围 对于 20% 的数据,1≤M≤N≤100 对于 50% 的数据,1≤M≤N≤1000 对于 100% 的数据,1≤M≤N≤105,0≤ai≤231−1 输入样例: 3 2 1 2 4 输出样例: 6 |
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 100010 * 31, M = 100010; int n, m; int s[M]; int tr[N][2], cnt[N], idx; void insert( int x, int v) { int p = 0; for ( int i = 31; i >= 0; i -- ) { int u = x >> i & 1; if (!tr[p][u]) tr[p][u] = ++ idx; p = tr[p][u]; cnt[p] += v; } } int query( int x) { int res = 0, p = 0; for ( int i = 31; i >= 0; i -- ) { int u = x >> i & 1; if (cnt[tr[p][!u]]) p =tr[p][!u], res = res * 2 + 1; else p = tr[p][u], res = 2 * res; } return res; } int main() { cin >> n >> m; for ( int i = 1; i <= n; i ++ ) { int x; scanf ( "%d" , &x); s[i] = s[i - 1] ^ x; } int res = 0; insert(s[0], 1); for ( int i = 1; i <= n; i ++ ) { if (i > m) insert(s[i - m - 1], -1); res = max(res, query(s[i])); insert(s[i], 1); } cout << res << endl; return 0; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步