Trie树其实就是将字符串或一串东西存储在一棵树上。举个栗子。假如我们存入了两串字符。
会发现我们存储在这个树,其实有很多好处。Trie树最多的两个操作就是插入和查询。先来说说插入的好处,这里先上一道经典例题。
我们这里是通过一个数组son[ ]来维护这棵树。这里我们用idx这个索引来标记,具体用途看下面代码。
int cnt[N] , son[N][26];
//idx相当于一个指引,如果没有到达最后的字符,那么就引导到第idx行
//如果已经到达最后一个字符,那么久引导到cnt[idx],记录该字符串一共有多少个
int idx;
void insert( char str[] )
{
int p = 0;
//这里使用了一个小技巧,字符型数组最后一位为‘\0’,其bool为false
for(int i = 0 ; str[i] ; i++)
{
int u = str[i] - 'a';
//有不同树的新点加入
if( !son[p][u] ) son[p][u] = ++idx;
p = son[p][u];
}
cnt[p]++;
}
如果还不是很懂这个idx的用法建议画一个简单的二维数组som来模拟一下。
接下来就是第二个问题了,怎么实现对每个字符串数量的记录呢?我们可以使用一个cnt[ ]数组进行记录,每次到达某个字符串最后一个字符就将该字符对应cnt数组所在的位置加1。
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int n;
//son数组用来模拟Trie,其中每一行表示一层
char str[N];
//cnt数组用来存储这个字符串出现的次数,与该字符串末尾字符有关
int cnt[N] , son[N][26];
//idx相当于一个指引,如果没有到达最后的字符,那么就引导到第idx行
//如果已经到达最后一个字符,那么久引导到cnt[idx],记录该字符串一共有多少个
int idx;
void insert( char str[] )
{
int p = 0;
//这里使用了一个小技巧,字符型数组最后一位为‘\0’,其bool为false
for(int i = 0 ; str[i] ; i++)
{
int u = str[i] - '0';
//有不同树的新点加入
if( !son[p][u] ) son[p][u] = ++idx;
p = son[p][u];
}
cnt[p]++;
}
int query( char str[] )
{
int p = 0;
for(int i = 0 ; str[i] ; i++)
{
int u = str[i] - '0';
//如果从未出现过该字符,表示没有录入
if( !son[p][u] ) return 0;
p = son[p][u];
}
return cnt[p];
}
int main()
{
scanf("%d" , &n );
char op;
while(n--)
{
cin >> op >> str;
if( op == 'I' ) insert(str);
else printf("%d\n" , query(str) );
}
return 0;
}
作者:阿柴
链接:https://www.acwing.com/activity/content/code/content/590031/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
接下来看一道比较难一点的题,最大异或数。
题目大概意思就是在给定的一组数中,每个数两两进行异或,最后找出异或数最大的数。如果这道题暴力做,至少是O(n^2)的级别。因为我们要穷举两两个数进行异或。由于我们刚刚了解了Tire树,可以发现使用Tire数会将问题时间复杂度变成O(n)。
先来看一下异或操作,其实就是两个二进制数每一位进行比较,如果相同,就得0,否则就为1。这题思路不难,举个栗子。
如果我们要找出2异或最大的数,先从最高位开始,可知2的二进制最高位为0,如果我们能找到一个最高位为1的,这样进行异或处理就可以得到一个大值。接下来每一位同理。当找不到不同的数时,我们只好顺着已有的数继续往下找。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10 , M = N * 31;
int n , a[N] , idx;
int som[M][2];
void insert(int x)
{
int p = 0;
for(int i = 30; ~i ; i-- )
{
//取x第30 - i位的二进制数是多少
int u = x >> i & 1;
//如果第p层的第u位没有出现过,就创立
if( !som[p][u] ) som[p][u] = ++idx;
//指向下一层
p = som[p][u];
}
}
int search(int x)
{
int p = 0 , res = 0;
for(int i = 30 ; ~i ; i-- )
{
int u = x >> i & 1;
//如果存在与该位相反的值
if( som[p][!u] )
{
p = som[p][!u];
//如果存在相反,先将该位加入,即乘2后在加上该位异或的值1
res = res * 2 + 1;
}
else
{
p = som[p][u];
res = res * 2 + 0;
}
}
return res;
}
int main()
{
scanf("%d" , &n );
for(int i = 0 ; i < n ; i++)
{
scanf("%d" , &a[i]);
insert(a[i]);
}
int res = 0;
for(int i = 0 ; i < n ; i++)
{
res = max(res , search(a[i]) );
}
cout << res << endl;
return 0;
}
作者:阿柴
链接:https://www.acwing.com/activity/content/code/content/596116/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。