trie树 最大异或对

又称字典树(踹树)

作用:用来快速的存储和查找字符串集合的数据结构

本质根据字符串的每个字符作为节点建树

在这里插入图片描述

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5 + 10;

struct trie{
	int son[N][26]; //一般题目都是全小写或者全大写或者全数字或者只有0 1
	// 因为这里假设都是小写字母,所以最多只有26条边
	int cnt[N]; // 以当前这个单词结尾的有多少个
	int idx; // 单链表用了哪一个点,下标是0的点,既是根节点,又是空节点
	
	void insert(char str[]){
		int p = 0; // 从根节点开始
		for(int i = 0; str[i]; i++){
			
			int u = str[i] - 'a';
			
			if(!son[p][u]) son[p][u] = ++idx;
			
			p = son[p][u];
		}
		
		cnt[p] ++; // 最后一定是到了最后一个单词,以这个节点结尾的单词数++
	}
	
	int query(char str[]){ // 返回这个单词出现了多少次
		
		int p = 0;
		for(int i = 0; str[i]; i++){
			int u = str[i] - 'a';
			if(!son[p][u]) return 0; // 这里不需要建边,只是查询
			p = son[p][u];
		}
		
		return cnt[p];
	}

}t;

int n;
char s[N];

int main(){
	cin >> n;
	while(n--){
		char op[2];
		scanf("%s%s", op, s);
		if(op[0] == 'I') t.insert(s);
		else cout << t.query(s) << endl;
	}
	
	return 0;
}

最大异或对

由异或的性质我们可以知道,如果一个数固定了(异或运算满足交换律),那么最高位于待定的数一定不同才能保证异或起来比较大

所以假设当前数最高位k位,那么把序列分成两部分:所有k位为0的数和所有k位为1的数;那么所有k位与该数的第k位不同的数与该数异或起来一定是比另一个集合要大的

或者某种分支他是不存在的,那么也就有唯一一条路径

这样每次的操作都保证了每一次的筛选都将是每一个集合中最大的那些,最后也就固定了哪个数是什么

在这里插入图片描述

这样画图来看,就特别像trie树,所以我们可以根据二进制位来对数进行存储

我们可以边插入边查找,不用一次都插入再查找;因为异或满足交换律,所以说我们边插入边查找也可以把每个数都判断一遍

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5 + 10;

int n;
int a[N];
int son[31 * N][2], idx;
// 每个数最多有31位,一个有N个数,一共需要31 * N个节点

void insert(int x){
    int p = 0;
    for (int i = 30; i >= 0; i--){
        int u = x >> i & 1;
        if(!son[p][u])
            son[p][u] = ++idx;
        p = son[p][u];
    }
}

int query(int x){
    int p = 0;
    int res = 0; // 记录路径上的数是什么
    for (int i = 30; i >= 0; i--){
        int u = x >> i & 1;
        if(son[p][!u]){ // 如果另一个方向存在,肯定走使答案变大的那个方向
            p = son[p][!u];
            res = (res << 1) + !u;
        } 
        else{
            p = son[p][u];
            res = (res << 1) + u;
        }       
    }
    return res;
}

int main(){
    cin >> n;
    for (int i = 1; i <= n; i++){
        cin >> a[i];
    }

    int ans = 0;
    for (int i = 1; i <= n; i++){
        insert(a[i]);

        int t = query(a[i]);

        ans = max(ans, a[i] ^ t);
    }

    cout << ans;

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值