学习Trie前提须知
\(Trie\)是一个字符串算法,它能够将很多个字符串映射在一棵树上,并且能够很快的判断一个串是否是另一个串的子串,也能够进行统计字符串出现次数等一些其他的东西,考题出现的频率不高,多作用于AC自动机的前置技能。 (甚至可以在Trie上DP)
算法内容
竞赛需要用到的点
1、\(Trie\)的结构体数组最好往大里开
2、\(Trie\)在字符串的题里受限都比较大,常与\(kmp\)混合用来进行AC自动机算法
Trie求字符串是否出现过略讲
\(Trie\)字典树,从名字就能看出是一棵树形结构了,每一个子树都是一个字符串或多个字符串。
\(Trie\)字典树与其他算法不同,直接从代码入手也许更好理解
将字符加入字典树
int tot;
void trieInsert(char *c, int rt) { //trieInsert(c, 0);
int len = strlen(c);
for (int i = 0; i < len; i++) {
int id = c[i] - 'a';
if(!trie[rt].son[id]) trie[rt].son[id] = ++tot;
rt = trie[rt].son[id];
} trie[rt].have = 1;
}
将字符串导入 \(Trie\) 中,从这个代码我们可以得到
- 根节点为空节点,每个节点最多可以接26个点
- 每个节点上代表为一个字符,末尾需要打上标记,不然若出现一个以当前字符为真前缀的字符就不好判断了
判断字符串是否存在
bool trieFind(char *c, int rt) {
int len = strlen(c);
for (int i = 0; i < len; i++) {
int id = c[i] - 'a';
if(!trie[rt].son[id]) return false;
rt = trie[rt].son[id];
}
if(!trie[rt].have) return false;
}
思路和上面相同 并无多大改动
看一道模板题于是他错误的点名开始了
//#define fre yes
#include <cstdio>
#include <cstring>
const int N = 800005;
struct Node {
int son[26];
int cnt;
bool have;
} trie[N];
int tot;
void trieInsert(char *c, int rt) {
int len = strlen(c);
for (int i = 0; i < len; i++) {
int id = c[i] - 'a';
if(!trie[rt].son[id]) trie[rt].son[id] = ++tot;
rt = trie[rt].son[id];
} trie[rt].have = 1;
}
int flag;
bool trieFind(char *c, int rt) {
int len = strlen(c);
for (int i = 0; i < len; i++) {
int id = c[i] - 'a';
if(!trie[rt].son[id]) return false;
rt = trie[rt].son[id];
}
if(!trie[rt].have) return false;
if(!trie[rt].cnt) {
trie[rt].cnt++;
return true;
} else {
flag = 1;
return true;
}
}
int main() {
static int n, m;
scanf("%d", &n);
char c[105];
for (int i = 1; i <= n; i++) {
scanf("%s", c);
trieInsert(c, 0);
}
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
scanf("%s", c);
if(trieFind(c, 0) && flag == 0) {
puts("OK");
} else if(trieFind(c, 0) && flag == 1) {
flag = 0;
puts("REPEAT");
} else puts("WRONG");
} return 0;
}