ZCMU1783 秋实大哥与快餐店

题目链接:http://acm.zcmu.edu.cn/JudgeOnline/problem.php?id=1783

题目大意:先输入n,然后保存这n个数字,然后输入m,表示m次操作。有两种操作,第一种是保存输入的一个数字,第二种是输入一个数字,找保存的数字和这个数字异或和最大的数字,输出这个保存的数字。(n,m<=100000 输入的数字<=1000000)

前言:讲这道题前先浅谈一下字典树。如果有n个小写字母,然后有m个查询,每个查询包含一个字母,问这个字母在n个字母中出现几次,怎样高效的查找?

1.for找n遍,复杂度O(n*m)

2.vis[c-'a']++,hash表存字母出现的个数,复杂度O(m)

换一个问题,如果有n个字符串,然后有m个查询,每个查询包含一个字符串,问这个字符串在n个字符串中出现几次,怎样高效的查找?

0.for 循环。

1.用map <string,int>,插入复杂度O(logn),查找复杂度O(mlogn) ,map找字符串是否匹配还要一个个去比较字符是否相等

2.类似上面的第二个方法,用hash表去找,但是要找一遍单词的每个字符是否在hash表里,相当于多次进行第一个问题的操作。复杂度明显比map低很多。所以问题就在于怎么去找,在第一个字符一样的前提,怎么去找下一个字符。

字典树的本质就是被查询的对象需要依次进行多次的hash查找,并且上一次的查找结果会影响到下一次的查找

什么时候用到字典树?查询的对象需要依次进行hash查找的时候。

比如:给定N个单词,查询N个单词中前缀为"abc"的是否存在,用hash去找a是否存在,a存在的话去找b,在去找c,这就是多次去找。前缀abc第一次去找a,找到后去x位置找b,而如果前缀是"cbb"的话,第一次找c,然后从y位置去找b,x和y的位置必然是不同,因为位置相同的话表示他们两个之前找的字符必然完全相同。这就是上一次的查找结果会影响到下次。

这里给个学习字典树的链接:https://www.cnblogs.com/TheRoadToTheGold/p/6290732.html

下面给出这道题的思路:

一般算法复杂度O(n*m) 在这里不赘述。下面讲AC解法。

要求出异或后的值最大,异或表示相同为0,不同为1。什么情况下值会最大?越高位出现0^1,则值越大。所以说把查询的值转成二进制,从高位开始,如果查询的数是0,则所有被查询的高位有没有1,是1则找0。然后在找下一位。有没有想到什么?这不就是字典树的本质:查询的对象需要依次进行多次的hash查找。

下面给出代码:

#include <iostream>
#include <cstring>
#include <stdio.h>
using namespace std;
struct node
{
    int l,r,ID;
}tree[(1<<22)+10];
int sum,bit[20]; 
void Create(int num)
{
    int pos=0,next;
    for(int j=19;j>=0;j--)
            {
                if(bit[j])
                {
                    if(tree[pos].r)
                    {
                        next=tree[pos].r;
                        pos=next;   
                    }
                    else
                    {
                        tree[pos].r=++sum;
                        next=tree[pos].r;
                        pos=next;
                    }
                    bit[j]=0;
                }
                else
                {
                    if(tree[pos].l)
                    {
                        next=tree[pos].l;
                        pos=next;   
                    }
                    else
                    {
                        tree[pos].l=++sum;
                        next=tree[pos].l;
                        pos=next;
                    }
                }
            }
            tree[pos].ID=num;
            return ;
        }
int main(int argc, char *argv[])
{
    int m,op,c,n,num,ct,tnum;
    while(cin>>n)
    {
        sum=0;
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++)
        {
            ct=0;
            scanf("%d",&num);
            tnum=num;
            while(num)
            {
                if(num%2==1)    bit[ct++]=1;
                else bit[ct++]=0;
                num/=2;
            }
            Create(tnum);
        }
        cin>>m;
        while(m--)
        {
            scanf("%d %d",&op,&c);
            ct=0;
            tnum=c;
            while(c)
            {
                if(c%2==1)  bit[ct++]=1;
                else bit[ct++]=0;
                c/=2;
            }
            if(op)
            {
                int pos=0,next;
                for(int i=19;i>=0;i--)
                {
                    if(bit[i]==0)
                    {
                        if(tree[pos].r)
                        {
                            next=tree[pos].r;
                            pos=next;
                        }
                        else
                        {
                            next=tree[pos].l;
                            pos=next;
                        }
                    }
                    else
                    {
                        if(tree[pos].l)
                        {
                            next=tree[pos].l;
                            pos=next;
                        }
                        else
                        {
                            next=tree[pos].r;
                            pos=next;
                        }
                        bit[i]=0;
                    }
                }
                printf("%d\n",tree[pos].ID);    
            }
            else
            {
                Create(tnum);
            }
        }
    }
    return 0;
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值