字符串的存储与查询
int son[N][26], cnt[N], idx;idx与链表作用相同,如果没有就++idx为它开一个
// 0号点既是根节点,又是空节点
// son[][]存储树中每个节点的子节点son[N][26]中26看情况,小写字母26,如果大小一起就52
// cnt[]存储以每个节点结尾的单词数量
// 插入一个字符串
void insert(char *str)
{
int p = 0;
for (int i = 0; str[i]; i ++ )//str[i]!='\0'
{
int u = str[i] - 'a';//化成数字
if (!son[p][u]) son[p][u] = ++ idx;
p = son[p][u];//标记单词,就是最后一个字母
}
cnt[p] ++ ;
}
// 查询字符串出现的次数
int query(char *str)
{
int p = 0;
for (int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a';
if (!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
- Trie字符串统计
维护一个字符串集合,支持两种操作:
“I x”向集合中插入一个字符串x;
“Q x”询问一个字符串在集合中出现了多少次。
共有N个操作,输入的字符串总长度不超过 105,字符串仅包含小写英文字母。
输入格式
第一行包含整数N,表示操作数。
接下来N行,每行包含一个操作指令,指令为”I x”或”Q x”中的一种。
输出格式
对于每个询问指令”Q x”,都要输出一个整数作为结果,表示x在集合中出现的次数。
每个结果占一行。
数据范围
1≤N≤2∗104
输入样例:
5
I abc
Q abc
Q ab
I ab
Q ab
输出样例:
1
0
1
#include <iostream>
using namespace std;
const int N = 100010; // 全局变量
int son[N][26], cnt[N], idx;
//son[N][26] 记录的是当前 每一个父节点 所有子树(a~z)的扩展
//比如 son[1][0]=2 表示 1 结点的一个值为 a 的子结点为结点 2
//cnt[N] 标记数组,负责在任一个节点的 idx编号 上标记字符,重复的单词会标记到同一节点
//idx 记录当前 新增 的点是第几个节点。
string str;
void insert(string str) //增加字符
{
int p = 0; // p 指向根节点。
for (int i = 0; str[i]; i ++ ) //如果检测到终止符,退出循环
{
int u = str[i] - 'a'; //转成数字
if (!son[p][u]) son[p][u] = ++ idx;
//如果当前 没有 这个节点,就在 第 p 个父节点下方第 u 个子节点上新增一个点,
//记为 idx+1.
p = son[p][u];
//将 p 指向它的子节点,然后继续进行增加 其所在节点下一个字符的操作
}
cnt[p] ++ ; //在终止位置标记 这个字符出现次数加1
}
int query(string str) //查找字符
{
int p = 0;
for (int i = 0; str[i]; i ++ ) //如果检测到终止符,退出循环
{
int u = str[i] - 'a'; //转成数字
if (!son[p][u]) return 0;
//如果当前 没有 找到这个字符,就返回 0
p = son[p][u];
//查找成功,将 p 指向它的子节点,然后继续进行查找下一个字符的操作
}
return cnt[p]; //返回当前字符在标记数组的标记次数。
}
int main()
{
int n;
scanf("%d", &n);
while (n -- )
{
string op;
cin >>op >>str;
if (op[0] == 'I') insert(str);
else printf("%d\n", query(str));
}
return 0;
}
利用 STL 的 map 用法直接统计单词
#include<bits/stdc++.h>
using namespace std;
int n;
string op,str;
map<string,int> cnt;
int main()
{
cin>>n;
while(n--)
{
cin>>op>>str;
if(op=="I") cnt[str]++;
if(op=="Q") cout<<cnt[str]<<endl;
}
return 0;
}