数据结构 01trie树【字典树】
trie树
trie树是一种高效存储和查询字符串集合的数据结构
概念:
【算法竞赛进阶指南】: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
题目:
维护一个字符串集合,支持两种操作: 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
代码:
#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; }
题目:
在给定的 N 个整数 A1,A2……AN 中选出两个进行 xor(异或)运算,得到的结果最大是多少? 输入格式 第一行输入一个整数 N。 第二行输入 N 个整数 A1~AN。 输出格式 输出一个整数表示答案。 数据范围 1≤N≤105, 0≤Ai<231 输入样例: 3 1 2 3 输出样例: 3
代码:
#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; }
题目:
农夫约翰在给他的奶牛们喂食时遇到了一个问题。 他共有 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
代码:
#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; }
题目:
给定一个非负整数数列 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
代码:
#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; }