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;
}
原文:https://www.cnblogs.com/Iamcookieandyou/p/14787413.html