【Trie(字典树)--模板】学习

今天早上学习字典树,学了一上午懂了思想。


一、儿子节点表示法

代码是学习大佬的,数组实现字典树;

next [ NODE ] [ 26 ] : 

NODE是节点编号 ,后面26指的是每个节点都有26个分叉,if next[ i ] [ j ]==1 ,意思是说到    编号i的字母   到达   j 对应的字母 是 ok 的;

如果加上了算法头文件,则数组名为 next 会CE!

v [ NODE ] :

v  数组用来保存各节点的信息;

#define NODE 1000005

int next[NODE][26];
int v[NODE];
int node;

void init()  //初始化;
{
    node=1;  //节点个数;
    memset(next[0],0,sizeof(next[0]));
}

void add(char *str)  //加入
{
    int cur=0,k;
    int len=strlen(str);
    for(int i=0;i<len;i++)
    {
        k=str[i]-'a';
        if(next[cur][k]==0)
        {
            memset(next[node],0,sizeof(node));
            v[node]=0;  //清空操作;
            next[cur][k]=node++;
        }
        cur=next[cur][k];
        v[cur]++;
    }
}

int cal(char *str)  //查询
{
    int cur=0,k;
    int len=strlen(str);
    for(int i=0;i<len;i++)
    {
        k=str[i]-'a';
        if(next[cur][k])
            cur=next[cur][k];
        else
            return 0;
    }
    return v[cur];
}
// 注意在这里v[ i ] 存的是到达 编号为 i 的这个节点次数;

可以对着下图模拟一波代码:

两个字符串nyoj 、nyist  ,


二、左儿子右兄弟表示法

由于儿子节点表示法会将很多的节点白白浪费,耗用了大量的空间。左儿子右兄弟表示法以时间换空间节省内存,利用该表示法建成的是一棵二叉树,每个节点的左儿子节点代表儿子,右儿子节点表示兄弟;

代码:

#include<bits/stdc++.h>
#define N 1010

struct node
{
    char c;
    int l,r;
    int val,cut;
}trie[N+10];
int node;

void init()
{
    node=1;
    memset(trie,0,sizeof(trie));
}

void add(char *s)
{
    int rt,i;
    for(rt=0;*s;s++,rt=i)
    {
        for(i=trie[rt].l;i;i=trie[i].r)
        {
            if(trie[i].c==*s)
                break;
        }
        
        if(i==0)
        {
            trie[node].r=trie[rt].l;
            trie[node].l=0;
            trie[node].c=*s;
            trie[node].val++;
            trie[rt].l=node;
            i=node++;
        }
    }
    trie[rt].cut++;
    
    return 0;
}

int cal(char *s)
{
    int rt;
    for(rt=0;*s;s++)
    {
        for(rt=trie[rt].l;rt;rt=trie[rt].r)
        {
            if(trie[rt].c==*s)
                break;
        }
        if(rt==0)
            return 0;
    }
    return ~;
}

模拟这份代码的时候总搞不懂,比如nyoj,nyist构建二叉树的过程:


----------------------


o节点被挤到i的右兄弟上了,明白了这一点就模拟好代码了;


三、01二叉树模板

题目类型 基本与异或相关,比如求序列两两亦或最大值、序列连续异或最大值等;

异或规则 是:相同为0,不同为1;

给定一个数x,为了得到异或后的最大值尽量寻找与 x 同位相异的数字,把数字转化为二进制串即01串,把这个串插入字典树中,要从高位开始插入,贪心思想!

代码:

int nex[N][2];
int val[N],num[N];
int node;

void init()
{
    node=1;
    memset(nex[0],0,sizeof(nex[0]));
}

void add(int c)
{
    int cur=0,k;
    for(int i=31;i<=0;i--)
    {
        k=(c>>i)&1;
        if(!nex[cur][k])
        {
            memset(nex[node],0,sizeof(nex[node]));
            val[node]=cut[node]=0;
            
            nex[cur][k]=node++;
        }
        cur=nex[cur][k];
        num[cur]++;
    }
    val[cur]=c;
}

int cal(int c)
{
    int cur=0,k;
    for(int i=31;i<=0;i--)
    {
        k=(c>>i)&1;
        if(nex[cur][1-k])     //同位相异,1-0=1,1-1=0;
            cur=nex[cur][1-k];
        else
            cur=nex[cur][k];
    }
    return c^val[cur];
}


小结:

经常保存的信息为:①某个节点用的次数 ②某个节点作为串的尾节点次数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值