#include <bits/stdc++.h>
using namespace std;
const int N = 100010; // 预设Trie树的最大节点数量
// son数组:第一维表示父节点编号,第二维表示字符(0-25对应a-z),值存储子节点编号
// cnt数组:记录以某个节点为结尾的字符串数量
// idx:当前可用的节点编号(从1开始分配)
int son[N][26], cnt[N], idx;
// 插入字符串到Trie树
void insert(char str[])
{
int p = 0; // 从根节点(0号节点)开始遍历
for(int i = 0; str[i]; i++) // 遍历字符串每个字符
{
int u = str[i] - 'a'; // 将字符转换为0-25的数字
/* 模拟示例:插入"abc"
1. 处理a(u=0): 检查根节点0的0号子节点是否存在
2. 若不存在则创建新节点(假设idx=1),p移动到1
3. 处理b(u=1): 检查节点1的1号子节点是否存在
4. 创建新节点(idx=2),p移动到2
5. 处理c(u=2): 检查节点2的2号子节点是否存在
6. 创建新节点(idx=3),p移动到3 */
if(!son[p][u]) // 如果当前字符对应的子节点不存在
son[p][u] = ++idx; // 创建新节点,分配递增的节点编号
p = son[p][u]; // 移动到子节点
}
cnt[p]++; // 在字符串结尾节点处增加计数
// 示例:插入完成后,cnt[3]会加1,表示"abc"出现一次
}
// 查询字符串出现次数
int query(char str[])
{
int p = 0;
for(int i = 0; str[i]; i++)
{
int u = str[i] - 'a';
/* 模拟示例:查询"ab"
1. 处理a(u=0): 检查根节点0的0号子节点存在(节点1)
2. p移动到1
3. 处理b(u=1): 检查节点1的1号子节点存在(节点2)
4. p移动到2
循环结束,返回cnt[2]的值(假设之前只插入过"abc",此处返回0) */
if(!son[p][u]) // 如果路径不存在
return 0; // 直接返回0
p = son[p][u]; // 沿路径向下移动
}
return cnt[p]; // 返回结尾节点的计数值
}
int main()
{
int n;
cin >> n;
while(n--)
{
char op[2];
char str[N];
cin >> op >> str;
if(op[0] == 'I')
{
insert(str);
/* 示例操作日志:
Insert "apple":
- 创建路径0->1(a)->2(p)->3(p)->4(l)->5(e)
- cnt[5] = 1 */
}
else
{
cout << query(str) << endl;
/* 示例查询:
Query "app":
- 路径存在:0->1(a)->2(p)->3(p)
- 返回cnt[3]的值(若只插入过"apple",此处返回0) */
}
}
return 0;
}
/*
数据结构可视化示例(插入"abc", "abd", "ab"后):
根节点0
└─a(0) → 节点1
└─b(1) → 节点2
├─c(2) → 节点3 (cnt=1)
└─d(3) → 节点4 (cnt=1)
cnt[2] = 1(对应"ab")
查询流程说明:
1. 查询"ab":到达节点2,返回cnt[2]=1
2. 查询"abc":到达节点3,返回cnt[3]=1
3. 查询"abe":在查找e(u=4)时路径中断,返回0
*/
本篇参考acwing算法基础课。