Trie 学习笔记
1 普通Trie
Trie 是一种字符串算法,支持两个操作:
- 插入一个字符串。
- 询问一个字符串出现的次数。
Trie 的插入:
- 首先建立一个数组,数组的类型是一个大小为 26 26 26 的数组,用来存储每一个点的儿子信息:如果该节点的儿子暂时不存在,那么这个节点为 0 0 0,否则为儿子节点的编号。
- 插入一个字符串,如果字符串当前的字符指向的位置存在那么直接走到存在的位置,否则先创建一个新的节点并且将这个位置指向下一个节点,并且走到这一个位置。插入完毕之后累加计数数组,代表这个字符串出现的次数。
比如说,依次插入了 cat
,car
,string
,str
,strlen
这五个字符串,图长这个样子:(丑勿喷)
Trie 的查询:
- 首先从根节点开始查找,如果字母不存在那么字符串不存在,如果一直存在那么返回当前这个单词的计数器。
容易发现插入和查询时间复杂度都是 O ( ∣ s ∣ ) O(|s|) O(∣s∣) 的。
2 Trie 练习题
2.1 HDU 2072
属实是坑到了。
范围: n ≤ 10000 n\le 10000 n≤10000。
多组数据,不给测试数据的组数,还会出现故意多空格的情况,注意这些坑点。
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
struct Trie
{
int next[26];
Trie()
{
memset (next, 0, sizeof next);
}
} trie[N];
char s[N], t[N];
int ccnt[N];
int cnt = 0, root = 0;
int ans = 0;
void init()
{
for (int i = 0; i <= cnt; i ++)
memset(trie[i].next, 0, sizeof trie[i].next);
for (int i = 0; i <= cnt; i ++)
ccnt[i] = 0;
ans = cnt = root = 0;
}
void insert(char *s)
{
int len = strlen(s);
int nowid = root;
for (int i = 0; i < len; i ++)
{
if (!trie[nowid].next[s[i] - 'a'])
trie[nowid].next[s[i] - 'a'] = ++ cnt;
nowid = trie[nowid].next[s[i] - 'a'];
}
if (!ccnt[nowid])
ans ++;
ccnt[nowid] ++;
}
int query(char *s)
{
int len = strlen(s);
int nowid = root;
for (int i = 0; i < len; i ++)
{
if (!trie[nowid].next[s[i] - 'a'])
return 0;
nowid = trie[nowid].next[s[i] - 'a'];
}
return ccnt[nowid];
}
int main()
{
while (cin.getline(s, N - 5))
{
if (cin.eof())
break;
if (!strcmp(s, "#"))
break;
init();
int cnt = 0;
int len = strlen(s);
for (int i = 0; i < len; i ++)
if (s[i] != ' ')
t[cnt ++] = s[i];
else if (cnt)
{
t[cnt ++] = 0;
insert(t);
cnt = 0;
}
if (cnt)
{
t[cnt ++] = 0;
insert(t);
}
cout << ans << '\n';
}
}
2.2 HDU 1251
n ≤ 1 0 6 n\le 10^6 n≤106。
求前缀数量的话插入到每一个位置计数器都自增 1 1 1 即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
struct Trie
{
int next[26];
Trie()
{
memset (next, 0, sizeof next);
}
} trie[N];
char s[N], t[N];
int ccnt[N];
int cnt = 0, root = 0;
int ans = 0;
void init()
{
for (int i = 0; i <= cnt; i ++)
memset(trie[i].next, 0, sizeof trie[i].next);
for (int i = 0; i <= cnt; i ++)
ccnt[i] = 0;
ans = cnt = root = 0;
}
void insert(char *s)
{
int len = strlen(s);
int nowid = root;
for (int i = 0; i < len; i ++)
{
if (!trie[nowid].next[s[i] - 'a'])
trie[nowid].next[s[i] - 'a'] = ++ cnt;
nowid = trie[nowid].next[s[i] - 'a'];
ccnt[nowid] ++;
}
}
int query(char *s)
{
int len = strlen(s);
int nowid = root;
for (int i = 0; i < len; i ++)
{
if (!trie[nowid].next[s[i] - 'a'])
return 0;
nowid = trie[nowid].next[s[i] - 'a'];
}
return ccnt[nowid];
}
int main()
{
while (cin.getline(s, N - 5))
{
if (strlen(s) == 0)
break;
insert(s);
}
while (cin.getline(s, N - 5))
cout << query(s) << '\n';
}