#include<iostream>usingnamespace std;constint N =1e6+10;// Trie树是一个集合,可以存储字符串// son二维数组中,每行代表一个节点,该行的每列都是它的儿子,最多26列代表一个节点最多26个儿子(题目中说了都是小写字母)// cnt[i]代表以序号为i的节点结尾的字符串有多少个// idx代表当前可用序号int son[N][26], cnt[N], idx;// 构造Trie树voidinsert(string s){// p代表当前节点的序号; 根节点序号为0,初始时p是根节点int p =0;for(int i =0; i < s.size(); i ++){// 将字符串中的26种小写字母映射到0~25int u = s[i]-'a';// 如果p节点中的所有子节点没有该字符,给该字符一个序号,相当于创建了新节点if(!son[p][u]) son[p][u]=++ idx;// 让p指向这个新节点
p = son[p][u];}// 字符串在树中插入结束后,最后一个元素作为叶子节点,标记以该节点结尾的字符串的个数多一个
cnt[p]++;}// 查询集合中某个字符串的个数intquery(string s){// 根节点序号为0,从根节点开始查int p =0;for(int i =0; i < s.size(); i ++){// 当前字符映射到0~25int u = s[i]-'a';// 如果p节点中的所有子节点没有该字符,说明该字符串在树中不存在,个数为0if(!son[p][u])return0;// 如果当前字符存在,p指向当前字符的节点
p = son[p][u];}// cnt[p]代表以p序号节点结尾的字符串的个数return cnt[p];}intmain(){int n;
cin >> n;while(n --){char op;
string x;
cin >> op >> x;if(op =='I'){insert(x);}elseif(op =='Q'){
cout <<query(x)<< endl;}}return0;}
2. 应用:寻找最大异或对:给定一个数组,找到数组中任意两个数异或后的最大值
#include<iostream>usingnamespace std;constint N =1e5+10;// M代表Trie数最多有多少节点// 因为每次往trie树加入一个数时最多可能加31个节点,又因为最多十万个数,那么最多有三百万个节点constint M =3e6+10;int a[N];// Trie树int son[M][2], idx;voidinsert(int x){int p =0;// x是int型整数,二进制总共右32位。其中最高位符号位不用管,还剩下31位,遍历每个二进制位for(int i =30; i >=0; i --){// 求当前位是0还是1int u = x >> i &1;if(!son[p][u]) son[p][u]=++ idx;
p = son[p][u];}}// 假如x是1,二进制是0001// 如果想让某个数y和x异或后值最大,那么y要尽量保证从高位开始就和x的高位相反,也就是说y的最好的可能是1110; intquery(int x){int p =0, res =0;for(int i =30; i >=0; i --){// 假如当前位是0int u = x >> i &1;// 希望在树中找到1if(son[p][!u]){
p = son[p][!u];// 求x与数y的最大异或值
res +=1<< i;}else p = son[p][u];}return res;}intmain(){int n;
cin >> n;for(int i =0; i < n; i ++)scanf("%d",&a[i]);// 构建Trie树for(int i =0; i < n; i ++)insert(a[i]);// 查询每个数和集合中的其他数的最大异或值int res =0;for(int i =0; i < n; i ++) res =max(res,query(a[i]));
cout << res << endl;return0;}