tire树的简单介绍和应用

文章介绍了如何使用字典树(Trie)数据结构进行字符串的快速插入与查询,以及在异或运算中的应用。在最大异或对问题中,通过预处理将数字转换为字典树,然后按位查询找到不同的数。此外,还展示了字典树在前缀和与滑动窗口问题中的结合使用,动态维护异或和的最大值。
摘要由CSDN通过智能技术生成

介绍:tire就是字典树,快熟存储和查询字符串的数据结构。

#include<iostream>

using namespace std;

const int N = 1e5 + 10;
int cnt[N],son[N][26];
int idx;//idx是关键,idx相当于单链表中的idx,记录下一个字符的位置
void insert(string 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(string 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];
}
int main()
{
    int t;
    
    cin>>t;
    while(t--)
    {
        char op[2];
        string str;
        cin>>op>>str;
        if(op[0]=='I') insert(str);
        else cout<<query(str)<<endl;
    }
    
    return 0;
}

 分析:本体是求最大的异或对,异或运算是根据二进制来计算的,所以我们可以把,所有数现预处理为字典树,然后再依次从高位开始查询,是否存在与二进制下当前位,不一样的数。

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1e5 + 10 , M = 3100000;//M表示结点个数
int son[M][2],idx;
int a[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,res=0;
    for(int i=30;i>=0;i--)
    {
        int u=x>>i&1;
        if(son[p][!u])//如果当前存在不一样的数,那么就是有用
        {
            res+=1<<i;
            p=son[p][!u];//!!!!
        }
        else p=son[p][u];
    }
    return res;
}
int main()
{
    
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
        insert(a[i]);
    }
    
    int res=0;
    for(int i=0;i<n;i++) res=max(res,query(a[i]));
    cout<<res<<endl;
    return 0;
}

应用:前缀和+滑动窗口

 

1、 假如 sum = a^b ,那么a=sum^b,

2、 一个数异或上一个数,相当于没异或,那么s[l~r]=s[r]^s[l-1]

利用这些性质可以推出本题

#include<iostream>
#include<algorithm>

using namespace std;

const int M = 3100000,N = 1e5 + 10;
int s[N],son[M][2],idx;
int cnt[M];
int n,m;
void insert(int x,int v)
{
    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];
        cnt[p]+=v;
    }
}

int query(int x)
{
    int p=0,res=0;
    for(int i=30;i>=0;i--)
    {
        int u=x>>i&1;
        if(cnt[son[p][!u]]) 
        {
            res+=1<<i;
            p=son[p][!u];
        }
        else p=son[p][u];
    }
    return res;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        s[i]=s[i-1]^x;
    }
    
    int res=0;
    insert(s[0],1);//因为一个数异或上0,还是0,又因为s[0]可能被用上,所以要加上
    for(int i=1;i<=n;i++)
    {
        if(i-1>=m) insert(s[i-1-m],-1);//当当前数,超过了窗口,就删去
        res=max(res,query(s[i]));
        insert(s[i],1);
    }
    
    cout<<res<<endl;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值