Trie三战含板子
Trie三战含板子
字典:一个字符串的集合称为字典。
字典串:在字典里的串称为字典串。
-
在处理字符串的时候,常会遇到这样的简单问题:给出一个字典,然后回答大量询问:输入一个字符串,判断它是否在字典中。
-
Trie 是一棵有根树,每个点至多有 |Σ| 个后继边,每条边上有一个字符。
-
每个点表示一个前缀:从跟到这个点的边上的字符顺次连接形成的字符串。
-
每个点还有一个终止标记:是否这个点代表的字符串是一个字典串。可以支持向 Trie 插入新字典串,删除字典串,查询某字符串是否是字典串,以及一些稍微复杂的查询。作为一个数据结构,它理所应当的还可以进行持久化。
-
这是一份处理小写字母字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51int id(char c) {//给出现的字符集编码,记得改offset部分
if (c >= 'a' && c <= 'z')
return c - 'a';
else if (c >= 'A' && c <= 'Z')
return c - 'A' + 26;
else
return c - '0' + 52;
}
struct Trie {//正常字母字符串trie
static constexpr int ALPHA = 26;
struct Node {
int cnt;
bool ended;
array<int, ALPHA> next;
Node() : cnt{0}, ended{false}, next{} {}
};
vector<Node> t;
Trie() { init(); }
void init() {
t.assign(2, {});
}
int newNode() {
t.emplace_back();
return t.size() - 1;
}
int add(const vector<int> &a) {
int p = 1;
for (auto x : a) {
if (t[p].next[x] == 0) {
t[p].next[x] = newNode();
}
p = t[p].next[x];
t[p].cnt++;
}
t[p].ended = true;
return p;
}
int add(const string &s, char offset = 'a') {
vector<int> a;
for (auto c : s) {
a.push_back(c - offset);
}
return add(a);
}
int cnt(int p) { return t[p].cnt; }
bool ended(int p) { return t[p].ended; }
int next(int p, int x) { return t[p].next[x]; }
int next(int p, char c, char offset = 'a') { return next(p, c - offset); }
int size() { return t.size(); }
};
Trie tr;01字典树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38struct Trie_bin {//保证第一次一定是先插入,查询不能先做
static constexpr int ALPHA = 2;
struct Node {
array<int, ALPHA> next;
Node() : next{} {}
};
vector<Node> t;
Trie_bin() { init(); }
void init() {
t.assign(2, {});
}
int newNode() {
t.emplace_back();
return t.size() - 1;
}
int add(const vector<int> &a) {
int p = 1;
for (auto x : a) {
if (t[p].next[x] == 0) {
t[p].next[x] = newNode();
}
p = t[p].next[x];
}
return p;
}
// 数字转01串vector
int add(int x, int width = 31) {//x必须小于2的width次方
vector<int> a;
for (int i = width - 1; i >= 0; i--) {
a.push_back((x >> i) & 1);
}
return add(a);
}
int next(int p, int x) { return t[p].next[x]; }
int size() { return t.size(); }
};
可持久化字典树:每次查询s[n]^x和区间内的某一个前缀异或和,异或起来的最大值
1 | struct Trie_per { |
1.给定 n 个不同的字符串 你可以任意指定字符之间的大小关系(即重定义字典序),求有多少个串可能成为字典序最小的串。(n <= 30000 , 字符串总长<= 300000 )
Sol:先构建出字典树,然后逐个字符串判定可行性。
考虑 $S_{i} S_{i} $当前将要走的边上的字母必须小于其他字母,我们可以对偏序关系建有向图表示。这等价于判定 26 个点的有向图上是否有环(拓扑排序)。
- 额外需要注意不能有任何其他串等于 $S_{i} $的前缀,即路径上不能有其他串的的终止节点。因为无论怎么定于字典序,前缀会比它的字典序小
1 | bool iscycle_direct(vector<vector<int>> &e, vector<int> °) { |
2.在给定的N个整数中选出两个进行xor运算,得到的结果最大是多少?
- 由于相同宽度的两个二进制数字的大小关系等价于字典序关系。
从高到低考虑 x 的每一个二进制位 bit:如果 y 的这一位也是 bit,则异或结果的这一位为 0;如果 y 的这一位是 !bit,则异或结果的这一位为1。将所有数字插入到 01Trie 中,枚举 x,在 01Trie 上寻找 y:从根出发,如果有 !bit 边,则走 !bit 边,否则只能走 bit 边。
实现的时候采用边查询边插入的办法
1 | Trie_bin tr; |
3.给出一个正整数数组 A,长度不超过 100*,* 000。定义区间异或和为区间所有数字异或起来的结果。求最大区间异或和。多个最大区间,取右端点小的。多个最大区间且存在右端点相等的,取区间长度最小的。
1 | Trie_bin tr; |
BZOJ - 2741 分块维护最大连续异或和 - Caturra - 博客园 (cnblogs.com)
BZOJ - 4260 01字典树+前后缀 - Caturra - 博客园 (cnblogs.com)
NKOJ8493 最大连续异或和 - Thermalrays - 博客园 (cnblogs.com)
Maximum Xor Secondary - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
牛客OI月赛12-提高组 C. 区间异或和异或区间最大值异或区间最小值(分治+字典树)_区间异或最值-CSDN博客
1 |
|
https://ac.nowcoder.com/discuss/287870?tdsourcetag=s_pctim_aiomsg#:~:text=【题解】牛客
