【模板】字典树 / Trie(前缀统计)
前缀统计(Trie 树模板题)
一、题目描述
给定 n 个模式串 s1, s2, ..., sn 和 q 次询问。
每次询问给定一个文本串 ti,回答:这 n 个字符串中,有多少个字符串以 ti 为前缀?
二、思路分析
Trie 树做法
把所有模式串插入一棵 Trie 树。Trie 的特点就是公共前缀共用同一条路径。
插入时,在路径上的每个节点都记录一个计数器 num:
num表示有多少个模式串经过这个节点- 即有多少个模式串以「从根到该节点的路径」为前缀
查询时,从根沿 ti 逐字符向下走:
- 如果能走完整个
ti,到达节点u,那么trie[u].num就是答案 - 如果中途某个字符对应的子节点不存在,直接返回 0
三、字符映射
题目中字符串大小写敏感,且包含:
- 小写字母
a ~ z:26 个 - 大写字母
A ~ Z:26 个 - 数字
0 ~ 9:10 个
共 62 个字符,需要映射到 0 ~ 61:
int getpos(char c)
{
if (c <= 'z' && c >= 'a') return c - 'a'; // 0 ~ 25
else if (c <= 'Z' && c >= 'A') return c - 'A' + 26; // 26 ~ 51
else return c - '0' + 52; // 52 ~ 61
}
ch 数组开到 65 足够。
四、AC 代码
#include<bits/stdc++.h>
using namespace std;
struct Node
{
int ch[65];
int num;
Node()
{
memset(ch, -1, sizeof(ch));
num = 0;
}
};
vector<Node> trie;
int createNode()
{
trie.emplace_back(Node());
return trie.size() - 1;
}
int getpos(char c)
{
if (c <= 'z' && c >= 'a') return c - 'a';
else if (c <= 'Z' && c >= 'A') return c - 'A' + 26;
else return c - '0' + 52;
}
void insert(string s)
{
int u = 0;
trie[u].num++;
for (int i = 0; i < s.size(); i++)
{
int pos = getpos(s[i]);
if (trie[u].ch[pos] == -1)
trie[u].ch[pos] = createNode();
u = trie[u].ch[pos];
trie[u].num++;
}
}
int query(string s)
{
int u = 0;
for (int i = 0; i < s.size(); i++)
{
int pos = getpos(s[i]);
if (trie[u].ch[pos] == -1) return 0;
u = trie[u].ch[pos];
}
return trie[u].num;
}
int main()
{
int T;
cin >> T;
while (T--)
{
int n, q;
cin >> n >> q;
trie.clear();
trie.emplace_back(Node()); // 根节点
string s;
for (int i = 0; i < n; i++)
{
cin >> s;
insert(s);
}
for (int i = 0; i < q; i++)
{
cin >> s;
cout << query(s) << endl;
}
}
return 0;
}
五、代码说明
1. 节点定义
struct Node
{
int ch[65]; // 62 个字符映射,开到 65 保险
int num; // 经过该节点的模式串数量
Node()
{
memset(ch, -1, sizeof(ch));
num = 0;
}
};
ch[i] = -1表示该子节点不存在num记录经过该节点的字符串数量
2. 插入时更新 num
根节点也要更新,因为空串也是所有字符串的前缀(如果询问是空串的话)。
trie[u].num++; // 进入节点时更新
路径上每个节点都要 num++,因为每个节点都对应一个前缀。
3. 查询
沿 ti 走到底,返回 trie[u].num。
中途走不下去说明没有模式串以 ti 为前缀,返回 0。
4. 多组数据的处理
每次新数据前:
trie.clear();
trie.emplace_back(Node());
清空 trie 并重建根节点。
六、复杂度
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 插入一个模式串 | O(L) | L 为字符串长度 |
| 查询一个前缀 | O(L) | 同上 |
| 总复杂度 | O((n + q) × L) | — |
空间复杂度:O(总字符数 × 字符集大小)。
七、总结
| 要点 | 内容 |
|---|---|
| 数据结构 | Trie 树 |
| 核心操作 | 插入时路径上每个节点 num++ |
| 查询 | 沿前缀走到底,返回末尾节点的 num |
| 关键 | 每个节点代表一个前缀,num 记录经过它的字符串数 |

浙公网安备 33010602011771号