今天早上学习字典树,学了一上午懂了思想。
一、儿子节点表示法
代码是学习大佬的,数组实现字典树;
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];
}
小结:
经常保存的信息为:①某个节点用的次数 ②某个节点作为串的尾节点次数