字典树&&01字典树专题&&对字典树的理解

对于字典树和01字典树的一点理解:

首先,字典树建树的过程就是按照每个数的前缀来的,如果你要存储一个全小写字母字符串,那么这个树每一个节点最多26个节点,这样的话,如果要找特定的单词的话,按照建树的方式找就可以了。

然后是01字典树,这个树在处理一些异或问题的时候特别好用,首先在存储一个树的过程中,我们是按照从高位开始的,如果是对于int型的,我们就从这个数的32位开始存储,不够的话,按照0补,这是建树的过程。再就是查询的时候,对于给定的数,我们先去找这一位上和他不同的,比如说,如果当前这个数的第i位上是1,那我们就找有没有一个数第i位上是0,如果没有的话,再去找第i位是0的数,然后按照从高位到低位寻找的话,就一定能寻找到满足情况的最优解。

入门:

题目链接:http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Index/problemdetail/pid/2828.html

用数组模拟。

我的理解:假设给你n个字符串,我们可以把有相同前缀的按照树的形式存储下来,这样就能够节省很多的空间,然后通过递归的形式来建树或者查找子串是否存在或者存在的次数。

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
char tmp[maxn];
int root[maxn][30];
int top;
void init()
{
    for(int i=0; i<top; i++)
    {
        for(int j=0; j<30; j++)
        {
            root[i][j]=0;
        }
    }
    top=0;
}
void add(char *str)
{
    int tot=0;
    int len=strlen(str);
    for(int i=0; i<len; i++)
    {
        int t=str[i]-'a';
        if(root[tot][t]==0)
            root[tot][t]=++top;
        tot=root[tot][t];
    }
}
bool judge(char *str)
{
    int len=strlen(str);
    int tot=0;
    for(int i=0; i<len; i++)
    {
        int t=str[i]-'a';
        if(root[tot][t]==0)
            return false;
        tot=root[tot][t];
    }
    return true;
}
int main()
{
    int n,m;
    top=0;
    while(~scanf("%d %d",&n,&m)&&(n+m))
    {
        init();
        for(int i=1; i<=n; i++)
        {
           // getchar();
            scanf("%s",tmp);
            add(tmp);
           // cout<<tmp<<endl;
        }
        for(int i=1; i<=m; i++)
        {
          //  getchar();
            scanf("%s",tmp);
            if(judge(tmp))
                printf("Yes\n");
            else
                printf("No\n");
        }
    }
    return 0;
}

A题:

题目链接:https://cn.vjudge.net/contest/276901#problem/A

题目大意:先输入若干个字符串,然后再每一次输入一个字符串,问你当前这个字符串再一开始输入的字符串中是前缀的有多少个?

AC代码:

#include<iostream>
#include<stack>
#include<string>
#include<cstring>
#include<stdio.h>
using namespace std;
# define ll long long
const int maxn = 2e6+100;
int rec[maxn][30];
int flag[maxn];
int tot;
char str1[maxn];
void add(char *str)
{
    int root=0;
    int len=strlen(str);
    for(int i=0; i<len; i++)
    {
        int t=str[i]-'a';
        if(!rec[root][t])
            rec[root][t]=++tot;
        flag[rec[root][t]]++;
        root=rec[root][t];

    }
}
int judge(char *str)
{
    int root=0;
    int len=strlen(str);
    for(int i=0; i<len; i++)
    {
        int t=str[i]-'a';
        if(rec[root][t]==0)
            return 0;
        root=rec[root][t];
        //cout<<i<<endl;
    }
    return flag[root];
}
void init()
{
    for(int i=0; i<tot; i++)
    {
        for(int j=0; j<30; j++)
        {
            rec[i][j]=0;
        }
    }
    tot=0;
}
int main()
{
    tot=0;
    while(gets(str1))
    {
        if(str1[0]=='\0') break;
        add(str1);
    }
    while(scanf("%s",str1)!=EOF)
    {
        printf("%d\n",judge(str1));
    }
    return 0;
}
   // init();
//    while(gets(str1))
//    {
//        if(str1[0]=='\0')
//            break;
//        scanf("%s",str1);
//        add(str1);
//        getchar();
//    }
//    while(scanf("%s",str1)!=EOF)
//    {
//        int ans=judge(str1);
//        //cout<<str1<<endl;
//        printf("%d\n",ans);
//    }
//    return 0;
//}

B题:

分割字符串

版本一:使用函数 stringstream (头文件<sstream>)

https://blog.csdn.net/weixin_35929051/article/details/52502486?tdsourcetag=s_pcqq_aiomsg(stringstream的使用说明)

#include<iostream>
#include<stack>
#include<string>
#include<cstring>
#include<sstream>
#include<stdio.h>
using namespace std;
# define ll long long
const int maxn = 1e6+100;
int rec[maxn][30];
int flag[maxn];
int tot,ans;
string tmp,str1;
void add(string str)
{
    int root=0;
    int len=str.size();
    for(int i=0; i<len; i++)
    {
        int t=str[i]-'a';
        if(!rec[root][t])
            rec[root][t]=++tot;
        root=rec[root][t];
    }
    if(flag[root]==0)
        ans++;
    flag[root]=1;
}
void init()
{
   // memset(flag,0,sizeof(flag));
    for(int i=0; i<=tot; i++)
    {
        flag[i]=0;
        for(int j=0; j<30; j++)
        {
            rec[i][j]=0;
        }
    }
    tot=0,ans=0;
}
int main()
{
    while(getline(cin,str1))
    {
        //cout<<str1<<endl;
        init();
        int p=0,t=0;
        if(str1=="#")
            break;
        stringstream ss(str1);
        while(ss>>str1)
        {
            add(str1);
        }
        cout<<ans<<endl;
       // cout<<tot<<endl;
    }
    return 0;
}

版本二:(超时)

#include<iostream>
#include<stack>
#include<string>
#include<cstring>
#include<stdio.h>
using namespace std;
# define ll long long
const int maxn = 1e6+100;
int rec[maxn][30];
int flag[maxn];
int tot,ans;
char tmp[maxn],str1[maxn];
void add(char *str)
{
    int root=0;
    int len=strlen(str);
    for(int i=0; i<len; i++)
    {
        int t=str[i]-'a';
        if(!rec[root][t])
            rec[root][t]=++tot;
        root=rec[root][t];
    }
    if(flag[root]==0)ans++;
    flag[root]=1;
}
void init()
{
    for(int i=0; i<=tot; i++)
    {
        flag[i]=0;
        for(int j=0; j<30; j++)
        {
            rec[i][j]=0;
        }
    }
    tot=0,ans=0;
}
int main()
{
    tot=0;
    while(gets(str1))
    {
        init();
        int p=0,t=0;
        if(str1[0]=='#')
            break;
        int len=strlen(str1);
        while(p<len)
        {
            while(str1[p]!=' '&&p<len)
            {
                tmp[t++]=str1[p];
                p++;
            }
            while(str1[p]==' '&&p<len)
            p++;
            add(tmp);
            t=0;
        }
        printf("%d\n",ans);
        getchar();
    }
    return 0;
}

01字典树:

首先输入n个数,然后每次输入一个数k,寻找输入的这个数和前面的n个数中的某一个最大的异或值?也就是k^s最大?

#include <iostream>
#include <string>
#include <deque>
#include <stack>
#include<cmath>
#include <algorithm>
#include<map>
using namespace std;
# define ll long long
# define inf 0x3f3f3f3f
const int maxn = 3e6+100;
ll flag[maxn];
int tot;
int sto[maxn][4];
void init()
{
    for(int i=0; i<=tot; i++)
    {
        flag[i]=0;
        for(int j=0; j<3; j++)
        {
            sto[i][j]=0;
        }
    }
}
void add(ll t)
{
    ll u=0;
    for(ll i=32; i>=0; i--)
    {
        ll tmp=(t>>i)&1;
        if(sto[u][tmp]==0)
            sto[u][tmp]=++tot;
        u=sto[u][tmp];
    }
    flag[u]=t;
}
ll query(ll t)
{
    ll u=0;
    for(ll i=32; i>=0; i--)
    {
        ll tmp=(t>>i)&1;
        if(sto[u][tmp^1])
            u=sto[u][tmp^1];
        else
            u=sto[u][tmp];
    }
    return flag[u];
}
int main()
{
    int T;
    scanf("%d",&T);
    tot=0;
    int Case=0;
    while(T--)
    {
        init();
        int n,m;
        scanf("%d %d",&n,&m);
        int tmp;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&tmp);
            add(tmp);
        }
        printf("Case #%d:\n",++Case);
        for(int i=1; i<=m; i++)
        {
            scanf("%d",&tmp);
            int ans=query(tmp);
            printf("%d\n",ans);
        }
    }
    return 0;
}

01字典树 

HDU - 5536 

具体思路:题目中说的是找三个不同的i,j,k,使得(a[i]+a[j])^a[k]的值最大,那么思路来了,首先第一步,我们先把所有的值全部存到字典树里面,然后两个for循环,每一次先把a[i]和a[j]去掉,然后再去求(a[i]+a[j])在字典树中的最值就可以了。

AC代码:

#include <iostream>
#include <string>
#include <deque>
#include <stack>
#include<cmath>
#include <algorithm>
#include<map>
using namespace std;
# define ll long long
# define inf 0x3f3f3f3f
# define ll_inf 1ll<<60
const int maxn = 1e6+100;
ll sto[maxn][3];
ll flag[maxn];
ll a[maxn],tot;
ll com[maxn];
ll Max(ll t1,ll t2)
{
    if(t1<t2)
        return t2;
    return t1;
}
void init()
{
    for(int i=0; i<=tot; i++)
    {
        flag[i]=0;
        com[i]=0;
        for(int j=0; j<3; j++)
        {
            sto[i][j]=0;
        }
    }
    tot=0;
}
void add(ll t)
{
    int u=0;
    for(int i=32; i>=0; i--)
    {
        int tmp=(t>>i)&1;
        if(sto[u][tmp]==0)
            sto[u][tmp]=++tot;
        flag[sto[u][tmp]]++;
        u=sto[u][tmp];
    }
    com[u]=t;//存储这一位上是哪个数
}
void Erase(ll t)
{
    int u=0;
    for(int i=32; i>=0; i--)
    {
        int tmp=(t>>i)&1;
        flag[sto[u][tmp]]--;//把这个数的路径走过的减去1,就相当于把这个数从树上去掉。
        u=sto[u][tmp];
    }
   // com[u]=0;
}
ll query(ll t)
{
    int u=0;
    for(int i=32; i>=0; i--)
    {
        int tmp=(t>>i)&1;
        if(sto[u][tmp^1]>0)//看一下当前这一位上是不是有不一样的
        {
            if(flag[sto[u][tmp^1]]>0)//如果有,就按照不同进行
                u=sto[u][tmp^1];
            else// 没有的话,就按照另外一种来进行
                u=sto[u][tmp];
        }
        else
        {
            if(flag[sto[u][tmp]]>0)//同理
                u=sto[u][tmp];
            else
                u=sto[u][tmp^1];
        }
    }
    return com[u]^t;
}
int main()
{
    int T;
    tot=0;
    scanf("%d",&T);
    while(T--)
    {
        init();
        int n;
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            add(a[i]);
        }
        ll maxx=0;
        for(int i=1; i<=n; i++)
        {
            Erase(a[i]);
            for(int j=i+1; j<=n; j++)
            {
                Erase(a[j]);
                maxx=Max(maxx,query(a[i]+a[j]));
                add(a[j]);
            }
            add(a[i]);
        }
        printf("%lld\n",maxx);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值