Trie树不仅可以处理字符串也可以处理整数(用二进制来进行树的存储)
- 字典树Trie字符串统计
- 最大异或对
int son[N][26];
其中存放的是:子节点对应的idx。其中son数组的第一维是:父节点对应的idx,第第二维计数是:其直接子节点(‘a’ - ‘0’)的值为二维下标。(一维表示父节点,二维表示父节点下面的子节点)
int cnt [N];
以“abc”字符串为例,最后一个字符—‘c’对应的idx作为cnt数组的下标。数组的值是该idx对应的个数。(存储字符串中最后一个字符的个数,即以最后一个字符结尾的字符串的个数)
int idx;
将该字符串分配的一个树结构中,以下标来记录每一个字符的位置。方便之后的插入和查找。(表示当前是第idx个节点)
idx : 要使用全局变量,这样才能表示不同子串且不会出现二异性(每次判断字符串中第一个字符有没有出现过,如果出现过顺着字符串往下查,如果没有出现过则在最后面,也就是idx+1的位置插入新字符)。但当idx是局部变量的时候会出现二异性,即(新建了一个字符串“abcd”)后再来创建“dbac”的时候,son[0][3]赋值为1,在插入b的时候,因为创建"abcd"的时候son[1][1]的值已经为1了,这时候idx就不会++,也不会在最后面插入新串了,这里就会产生误差。所以idx得使用全局变量来进行节点的插入。
具体看代码以及注释吧!!!!
维护一个字符串集合,支持两种操作:
I x 向集合中插入一个字符串 x;
Q x 询问一个字符串在集合中出现了多少次。
共有 N 个操作,输入的字符串总长度不超过 105,字符串仅包含小写英文字母。
输入格式
第一行包含整数 N,表示操作数。
接下来 N 行,每行包含一个操作指令,指令为 I x 或 Q x 中的一种。
输出格式
对于每个询问指令 Q x,都要输出一个整数作为结果,表示 x 在集合中出现的次数。
#include <iostream>
using namespace std;
const int N=2*1e4+10;
int son[N][26];
int cnt[N]; int idx=0;
//表示当前的节点的个数,如果找不到当前字符是需要再最后面idx+1的位置插入新节点
//如果idx是局部变量,再后面插入新串会根据之前串的痕迹改变当前串的构造结果就会产生误差
void Insert(char str[])
{ int p=0;
for(int i=0;str[i];i++)
{ int u=str[i]-'a'; //映射到0-25
if(son[p][u]==0) //如果当前节点p不存在子节点u,就在最后面idx+1的位置插入u节点
son[p][u]=++idx; //没有整个节点就创建节点
p=son[p][u]; //如果节点存在,就将p下移
}
cnt[p]++; //字符全部建完,p已经到叶节点了,将以p结尾的字符串做标记++
}
int que(char str[N])
{ int p=0;
for(int i=0;str[i];i++)
{ int u=str[i]-'a';
if(son[p][u]==0) //表示当前字符不存在,就直接返回没有这个字符串
return 0;
p=son[p][u]; //如果存在,p就下移
}
return cnt[p]; //返回最后一个字符有没有出现,实际上是返回结尾字符的个数,
}
int main()
{
int n;
cin>>n;
char str[N];
while(n--)
{ int flag=0;
char ch;
cin>>ch>>str;
if(ch=='I')
Insert(str);
else {flag=que(str);
cout <<flag<<"\n";}
}
}
- 前缀统计
给定 N 个字符串 S1,S2…SN,接下来进行 M 次询问,每次询问给定一个字符串 T,求 S1∼SN 中有多少个字符串是 T 的前缀。
输入字符串的总长度不超过 106,仅包含小写字母。
输入格式
第一行输入两个整数 N,M。
接下来 N 行每行输入一个字符串 Si。
接下来 M 行每行一个字符串 T 用以询问。
输出格式
对于每个询问,输出一个整数表示答案。
输入样例:
3 2
ab
bc
abc
abc
efg
输出样例:
2
0
#include <iostream>
using namespace std;
const int N=1E6+10;
int idx;
char str[N];
int son[N][26];
int cnt[N];
void Insert(char str[N])
{
int p=0;
for(int i=0;str[i];i++)
{
int u=str[i]-'a';
if(son[p][u]==0) son[p][u]=++idx;
p=son[p][u];
}
cnt[p]++;
}
int que(char str[])
{
int p=0;int res=0;
for(int i=0;str[i];i++)
{
int u=str[i]-'a';
if(!son[p][u]) return res;
p=son[p][u]; //父节点下移到子节点
res+=cnt[p]; //累加上以当前节点结尾的字符串的个数
}
return res;
}
int main()
{
int n,m;
cin>>n>>m;
while(n--)
{
cin>>str;
Insert(str);
}
while(m--)
{ cin>>str;
printf("%d\n",que(str));
}
}
- 最大异或对
在给定的 N 个整数 A1,A2……AN 中选出两个进行 xor(异或)运算,得到的结果最大是多少?
输入格式
第一行输入一个整数 N。
第二行输入 N 个整数 A1~AN。
输出格式
输出一个整数表示答案。
数据范围
1≤N≤105,
0≤Ai<231
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1E5+10,M=32*N;
int son[M][2],a[N];
int idx;
int insert(int x)
{
int p=0;
for(int i=31; i>=0; i--) //将每个数以二进制形式插入
{
int u=x>>i&1; //右移,找到第i位
if(!son[p][u])
son[p][u]=++idx;
p=son[p][u];
}
}
int query(int x)
{
int res=0; //对异或最大值的那个数赋值为0
int p=0; //根节点为0
for(int i=31; i>=0; i--)
{
int u=x>>i&1; //找到第i位
if(son[p][!u]) //看是否存在与它第i位值相反的那个树,如果u=0就找1,u=1就找0
{
res=res*2+1; //对这个数进行累加*2+1;
p=son[p][!u]; //同样的要移动到这个树的另一边的下个结点
}
else
{
res=res*2; //这里说明找不到相反的那个树,就直接*2+0
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++)
{
int x=query(a[i]); //每次查询到与树中所有树异或值最大的那个数并返回
res=max(res,x); //比较找出最大值
}
cout<<res;
}