C++Trie——最大异或数

Trie树其实就是将字符串或一串东西存储在一棵树上。举个栗子。假如我们存入了两串字符。

在这里插入图片描述

会发现我们存储在这个树,其实有很多好处。Trie树最多的两个操作就是插入和查询。先来说说插入的好处,这里先上一道经典例题。

在这里插入图片描述
在这里插入图片描述

我们这里是通过一个数组son[ ]来维护这棵树。这里我们用idx这个索引来标记,具体用途看下面代码。

int cnt[N] , son[N][26];
//idx相当于一个指引,如果没有到达最后的字符,那么就引导到第idx行
//如果已经到达最后一个字符,那么久引导到cnt[idx],记录该字符串一共有多少个
int idx;

void insert( char str[] )
{
    int p = 0;
    //这里使用了一个小技巧,字符型数组最后一位为‘\0’,其bool为false
    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]++;
}

如果还不是很懂这个idx的用法建议画一个简单的二维数组som来模拟一下。

在这里插入图片描述

接下来就是第二个问题了,怎么实现对每个字符串数量的记录呢?我们可以使用一个cnt[ ]数组进行记录,每次到达某个字符串最后一个字符就将该字符对应cnt数组所在的位置加1。

#include<iostream>
#include<cstring>

using namespace std;

const int N = 1e5 + 10;

int n;
//son数组用来模拟Trie,其中每一行表示一层
char str[N];
//cnt数组用来存储这个字符串出现的次数,与该字符串末尾字符有关
int cnt[N] , son[N][26];
//idx相当于一个指引,如果没有到达最后的字符,那么就引导到第idx行
//如果已经到达最后一个字符,那么久引导到cnt[idx],记录该字符串一共有多少个
int idx;

void insert( char str[] )
{
    int p = 0;
    //这里使用了一个小技巧,字符型数组最后一位为‘\0’,其bool为false
    for(int i = 0 ; str[i] ; i++)
    {
        int u = str[i] - '0';
        //有不同树的新点加入
        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] - '0';
        //如果从未出现过该字符,表示没有录入
        if( !son[p][u] ) return 0;

        p = son[p][u];
    }

    return cnt[p];
}


int main()
{
    scanf("%d" , &n );
    char op;
    while(n--)
    {
        cin >> op >> str;

        if( op == 'I' ) insert(str);
        else printf("%d\n" , query(str) );
    }

    return 0;
}

作者:阿柴
链接:https://www.acwing.com/activity/content/code/content/590031/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

接下来看一道比较难一点的题,最大异或数。

在这里插入图片描述

题目大概意思就是在给定的一组数中,每个数两两进行异或,最后找出异或数最大的数。如果这道题暴力做,至少是O(n^2)的级别。因为我们要穷举两两个数进行异或。由于我们刚刚了解了Tire树,可以发现使用Tire数会将问题时间复杂度变成O(n)。

先来看一下异或操作,其实就是两个二进制数每一位进行比较,如果相同,就得0,否则就为1。这题思路不难,举个栗子。

在这里插入图片描述

如果我们要找出2异或最大的数,先从最高位开始,可知2的二进制最高位为0,如果我们能找到一个最高位为1的,这样进行异或处理就可以得到一个大值。接下来每一位同理。当找不到不同的数时,我们只好顺着已有的数继续往下找。

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1e5 + 10 , M = N * 31;
int n , a[N] , idx;
int som[M][2];

void insert(int x)
{
    int p = 0;
    for(int i = 30; ~i ; i-- )
    {
        //取x第30 - i位的二进制数是多少
        int u = x >> i & 1;
        //如果第p层的第u位没有出现过,就创立
        if( !som[p][u] ) som[p][u] = ++idx;
        //指向下一层
        p = som[p][u];
    }
}

int search(int x)
{
    int p = 0 , res = 0;

    for(int i = 30 ; ~i ; i-- )
    {
        int u = x >> i & 1;
        //如果存在与该位相反的值
        if( som[p][!u] )
        {
            p = som[p][!u];
            //如果存在相反,先将该位加入,即乘2后在加上该位异或的值1
            res = res * 2 + 1;
        }
        else
        {
            p = som[p][u];

            res = res * 2 + 0;
        }
    }

    return res;
}

int main()
{
    scanf("%d" , &n );

    for(int i = 0 ; i < n ; i++)
    {
        scanf("%d" , &a[i]);

        insert(a[i]);
    }

    int res = 0;

    for(int i = 0 ; i < n ; i++)
    {
        res = max(res , search(a[i]) );
    }

    cout << res << endl;

    return 0;

}

作者:阿柴
链接:https://www.acwing.com/activity/content/code/content/596116/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值