大家都用过搜索引擎吧,在输入关键字时,下面的提示框会不断快速变化,里面的内容是一些以目前输入关键字开头的搜索记录,而且我们每次的搜索也会被加入到数据库中。
这个功能是怎么实现的呢?总不能遍历整个数据库呀,请看下文Trie树。
Trie树的基本功能是在支持插入、删除的情况下动态维护以一个前缀开头的所有字符串,那其存储有何特点?
刚才的叙述可能有些不好理解,让我们来看一个具体的案例,如图为以字符串"az",“app”,“apple”,“iak”,“ice”,“iakioi”,"iaknoi"构造的Trie树:
为方便实现,假设所有字符串均由小写字母构成。
初始化
令
N
N
N为需要的结点个数(即所有字符串长度之和),令
L
L
L为每个结点最大子结点数(即字符串每位有多少种可能)。
const int N = 1e5 + 5, L = 26;
struct Trie {
int t[N][L], cnt[N], k;
void init() {
memset(t, -1, sizeof t);
memset(cnt, 0, sizeof cnt);
k = 0;
}
};
插入
在树中找到其前缀的链,若不能完全包含这个字符串则新建剩下的结点。
void insert(char *s) {
int u = 0; // u: 当前结点
for (int i = 0; s[i]; ++i) { // 寻找前缀链
int &v = t[u][s[i] ^ 'a']; // 下一位结点
if (v == -1) { // 若没有:新建
v = ++k;
}
u = v; // 迭代
}
++cnt[u]; // 记录结束位置
}
删除
同理,沿链寻找其结束结点,将此位置结束的字符串数
−
1
-1
−1即可。
void erase(char *s) {
int u = 0;
for (int i = 0; s[i]; ++i) {
int v = t[u][s[i] ^ 'a'];
if (v == -1) {
return ;
}
u = v;
}
if (cnt[u]) {
--cnt[u];
}
}
查询
同理,返回:此字符串出现次数。
int find(char *s) {
int u = 0;
for (int i = 0; s[i]; ++i) {
int v = t[u][s[i] ^ 'a'];
if (v == -1) {
return 0;
}
u = v;
}
return cnt[u];
}
最终代码
const int N = 1e5 + 5, L = 26;
struct Trie {
int t[N][L], cnt[N], k;
void init() {
memset(t, -1, sizeof t);
memset(cnt, 0, sizeof cnt);
k = 0;
}
void insert(char *s) {
int u = 0;
for (int i = 0; s[i]; ++i) {
int &v = t[u][s[i] ^ 'a'];
if (v == -1) {
v = ++k;
}
u = v;
}
++cnt[u];
}
int find(char *s) {
int u = 0;
for (int i = 0; s[i]; ++i) {
int v = t[u][s[i] ^ 'a'];
if (v == -1) {
return 0;
}
u = v;
}
return cnt[u];
}
void erase(char *s) {
int u = 0;
for (int i = 0; s[i]; ++i) {
int v = t[u][s[i] ^ 'a'];
if (v == -1) {
return ;
}
u = v;
}
if (cnt[u]) {
--cnt[u];
}
}
};
题意:给定一个数组
a
1...
n
a_{1...n}
a1...n,求
m
a
x
{
a
i
x
o
r
a
j
}
max\{a_i xor a_j\}
max{aixoraj}。
题解:考虑在线处理,求出目前值和之前值的最大异或再取最大,如何求出?因为异或为二进制运算,则从前往后每次考虑当前位最优,即如果有的话就取目前二进制位的取反,这里就需要Trie的维护,只不过存储的不是字符串而是二进制位。
题意:给定一个字符集
S
S
S和一个字符串
T
T
T,求用
S
S
S中字符串拼接成
T
T
T的方案数。
题解:很容易想到dp,定义
f
i
f_i
fi为
T
T
T前
i
i
i位的答案;递推式也显而易见,若
T
j
.
.
.
i
T_{j...i}
Tj...i在
S
S
S中出现过,
f
i
+
=
f
j
−
1
f_i+=f_j-1
fi+=fj−1,这里是否出现就需要用Trie维护。
题意:给定字符集,求其中任意两个字符串最长公共前缀的期望值。
题解:我们知道
E
=
∑
P
×
X
E=\sum{P\times X}
E=∑P×X,所以我们可以在以此字符集建立的Trie中枚举当前点
u
u
u是最长公共前缀结尾的情况数再除以总情况数即可。对于以
u
u
u为结尾的情况,我们分三种情况讨论:第一种,两个串的结尾在
u
u
u两个子树中;第二种,一个以
u
u
u结尾,另一个以其子树中点结尾;第三种,均以
u
u
u结尾。
今天的复习内容就到这里,感谢收看~