一、字典树
字典树是一种用于实现字符串快速检索的多叉树结构,又称前缀树、Trie
下面的字典树以字符串为例:
字典树的每个结点都存储有若干个指针,如果在插入或者检索的过程中,扫描(历遍)到一个字符c,就沿着当前结点的c字符指针向下走
nex数组的第一维是指针的编号,第二维是可能出现的字符(如a~z对应0~26),数组的内容存储下一个指针的编号
ans数组记录能到达该点(以到该点的字符串为前缀)的字符串的个数
send数组记录在该点结尾的字符串的个数(可以开成bool数组记录是否存在到该点的字符串)
int nex[maxn][26], cnt = 0;
int ans[maxn];
int send[maxn];
char s[20];
void insert(char* s)
{
int len = strlen(s);
int p = 0;
for (int i = 0; i < len; i++)
{
int c = s[i] - 'a';
if (!nex[p][c])
nex[p][c] = ++cnt; //如果没有,就添加结点
p = nex[p][c];
//ans[p]++; //路径上的每个点都记录
}
send[p]++; //以该点结尾的字符串又send[i]个
}
int finds(char* s)
{
int len = strlen(s);
int p = 0;
for (int i = 0; i < len; i++)
{
int c = s[i] - 'a';
if (!nex[p][c])
return 0;
p = nex[p][c];
}
return send[p]; //返回字符串出现次数
//return ans[p];
}
优化:
1、不用string而用char数组
2、将nex数组可以改成map、ans记录可以改成unorder_map
map<pair<int, char>, int>nex;
unordered_map<int, int>ans;
ll cnt;
char s[maxn];
void insert()
{
int len = strlen(s);
int p = 0;
for (int i = 0; i < len; i++)
{
int c=s[i];
if (!nex[{p, s[i]}])
nex[{p, s[i]}] = ++cnt; //如果没有,就添加结点
p = nex[{p, s[i]}];
ans[p]++;
}
}
int query()
{
int len = strlen(s);
int p = 0;
for (int i = 0; i < len; i++)
{
int c = s[i];
if (!nex[{p, s[i]}])
return 0;
p = nex[{p, s[i]}];
}
return ans[p];
}
二、 01字典树
01字典树常用来处理异或的问题
把一个数转化为2进制,然后从高到低位历遍,把二进制形式当成字符串,由此来建树
int nex[maxn*32][2], nend[maxn * 32], cnt;
ll val[maxn * 32];
void insert(ll x)
{
int p = 0;
for (int i = 32; i >= 0; i--)
{
int u = x >> i & 1; //从最高位开始历遍
if (!nex[p][u])
nex[p][u] = ++cnt;
p = nex[p][u];
nend[p]++;
}
val[p] = x; //编号为p的值
}
ll query(ll x)
{
int p = 0;
for (int i = 32; i >= 0;i--)
{
int u = (x >> i) & 1;
if (nex[p][u ^ 1]) //想要求与x异或值最大的,那么尽量走与x为相异的路径
p = nex[p][u ^ 1];
else
p = nex[p][u];
}
return val[p];
}