简介
引入
字典树 / 前缀树
原理
Trie 树是一种多叉树的结构,每个节点保存一个字符,一条路径表示一个字符串。
功能 : 高效的存储insert和查找query字符串集合的数据结构
核心思想 : 空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的
应用场景 : 统计和排序大量的字符串 【注意有比较多的公共前缀这些字符串】
- 文本词频统计
- 前缀匹配
- 字符串检索
模板代码
向集合中插入一个字符串 x
void insert(char str[])
{
int p = 0; // 指向根节点
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] ++ ;
}
询问一个字符串在集合中出现了多少次
int query(char str[])
{
int p = 0;
for(int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a';
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
应用
835. Trie字符串统计
题目
分析
这里就是trie树地应用
代码
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int son[N][26], cnt[N], idx;
char op[2], str[N];
void insert(char str[])
{
int p = 0; // 指向根节点
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] ++ ;
}
int query(char str[])
{
int p = 0;
for(int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a';
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
int main()
{
int n;
scanf("%d", &n);
while(n -- )
{
scanf("%s%s", op, str);
if(op[0] == 'I') insert(str);
else printf("%d\n", query(str));
}
return 0;
}
143. 最大异或对
题目
分析
异或规则:1^1 = 0 0 ^ 0 = 0 1 ^ 0 = 1;
所以根据规则,如果两个数地二进制表示中,从个位中表示^差距越大地则最后地结果越大。
代码
#include <iostream>
using namespace std;
const int N = 100010, M = 31 * N;
int n;
int a[N];
int son[M][2], idx;
void insert(int x)
{
int p = 0;
for(int i = 30; i >= 0; i --)
{
int u = x >> i & 1;
if(!son[p][u]) son[p][u] = ++ idx;
p = son[p][u];
}
}
int query(int x)
{
int p = 0, res = 0;
for(int i = 30; i >= 0; i --)
{
int u = x >> i & 1;
if(son[p][!u])
{
p = son[p][!u];
res = res * 2 + !u;
}
else
{
p = son[p][u];
res = res * 2 + u;
}
}
return res;
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i ++) scanf("%d", &a[i]);
int res = 0;
for(int i = 0; i < n; i ++)
{
insert(a[i]);
int t = query(a[i]);
res = max(res, a[i] ^ t);
}
printf("%d", res);
return 0;
}
题单
转载来自Trie 树
level1
P2580 于是他错误的点名开始了
PHONELST - Phone List