trie树
trie树又叫字典树
trie 高效地存储和查找字符串集合的数据结构
本质根据字符串的每个字符作为节点建树
凡是用到trie树,字符不会太多
# include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int cnt[N] // 以当前这个单词结尾的有多少个
int son[N][26];//因为这里假设都是小写字母,所以最多只有26条边
int idx; // 单链表用了哪一个点,下标是0的点,既是根节点,又是空节点
char str[N];
void insert(char str[]){
int h = 0;
for (int i = 0; str[i]; i ++){
int t = str[i] - 'a';
if (!son[h][t]) son[h][t] = ++ idx;
h = son[h][t];
}
cnt[h] ++;
}
int query(char str[]){
int h = 0;
for (int i = 0; str[i]; i ++){
int t = str[i] - 'a';
if (!son[h][t]) return 0;
h = son[h][t];
}
return cnt[h];
}
int main(){
int n;
cin >> n;
while (n --){
char p;
cin >> p >> str;
if (p == 'I') insert(str);
else cout << query(str) << endl;
}
return 0;
}
这个idx是什么呢,是新需要插入的节点的编号,我们知道trie高效的原因之一是储存了公共的前缀,所以这些前缀节点再次插入的时候,是不需要另外储存的,idx只是需要新储存的节点编号。
如果son[i]这一行由元素不为0,那么它表示的是某个字母,至于是哪个字母下一段代码再分解。它储存的值,是下一个新插入元素的位置。注意如果要插入停止符,也会单独占用一个idx。
最大异或对
异或
异或是一种二进制的位运算,符号以 XOR 或 ^ 表示。
运算规则
相同为0,不同为1,即
1 ^ 1 = 0
0 ^ 0 = 0
1 ^ 0 = 1
由运算规则可知,任何二进制数与零异或,都会等于其本身,即 A ^ 0 = A。
异或性质
交换律: A ^ B = B ^ A
结合律: ( A ^ B ) ^ C = A ^ ( B ^ C )
自反性: A ^ B ^ B = A
因此要求最大异或对,在二进制下,位只能为1 或 0。那么只要让1异或0 即可,若实在找不到不同的,只能异或相同的了
可以看到它的结构跟trie树很相似
为什么可以边插入边查找
我们可以边插入边查找,不用一次都插入再查找;因为异或满足交换律,所以说我们边插入边查找也可以把每个数都判断一遍 举个例子 a b 是最大异或对,a 在前 b在后,a只会与它前面的判断,判断不到a,但b会与b之前的判断,会判断到a
为什么二进制左移一位等于乘以2
原理很简单的,就比如一个十进制的数,例如2,左移一位,后面补0,就是20,,相当于乘以10,二进制的左移相当于乘以2,右移相当于除以2
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N] ,son[31*N][2];
// 每个数最多有31位,一个有N个数,一共需要31 * N个节点
int idx;
void insert(int x){
int h;
for (int i = 30; i>= 0; i --){
int t = x >> i & 1; //求二进制第一位
if (!son[h][t]) son[h][t] = ++ idx;
h = son[h][t];
}
}
int query(int x){
int h = 0;
int res = 0;// 记录路径上的数是什么
for (int i = 30; i>= 0; i --){ // 若另一个方向存在,就走该方向
int t = x >> i & 1;
if (son[h][!t]){
h = son[h][!t];
res = (res << 1) + !t; //左移并加上!t
}
else {//找不到只能走相同的路径了
h = son[h][t];
res = (res << 1) + t;
}
}
return res;
}
int main(){
int n;
cin >> n;
int ans = 0;
for (int i = 1; i <= n; i ++){
cin >> a[i];
insert(a[i]);
ans = max(ans , a[i] ^ query(a[i]));
}
/*不理解可以先建完树再查询
for (int i = 1; i <= n; i++){
cin >> a[i];
insert(a[i]);
}
for (int i = 1; i <= n; i ++){
ans = max(ans , a[i] ^ query(a[i]));
}
*/
cout << ans;
return 0;
}