Trie🌳
又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
性质
它有3个基本性质:
根节点不包含字符,除根节点外每一个节点都只包含一个字符; 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串; 每个节点的所有子节点包含的字符都不相同。
应用
串的快速检索
给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。
在这道题中,我们可以用数组枚举,用哈希,用字典树,先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。
“串”排序
给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出
用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。
最长公共前缀
对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为当时公共祖先问题。
模板
int son[N][26], cnt[N], idx;
// 0号点既是根节点,又是空节点
// son[][]存储树中每个节点的子节点
// cnt[]存储以每个节点结尾的单词数量
// 插入一个字符串
void insert(char *str)
{
int p = 0;
for (int i = 0; str[i]; i ++ )
{
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];
}
题目
思路
代码
#include<iostream>
using namespace std;
const int N=100005;
int son[N][26];//第N个字母的儿子是什么,当前节点最多有26个分支
int cnt[N];//以当前字母结尾的字符串出现了多少次
int idx;//
string str;
void insert(string str){
int p=0;
for(int i=0;i<str.size();i++){
int u=str[i]-'a'; //把当前字符转化为数字
if(!son[p][u]){ //不存在当前点,那么创建一个点
son[p][u] = ++idx;
}
p=son[p][u]; //如果存在当前点,那么直接到当前点来
}
cnt[p]++; //标记以当前字符结尾的字符串个数++
}
int query(string str){
int p=0;
for(int i=0;i<str.size();i++){
int u=str[i]-'a';
if(!son[p][u]){
return 0;
}
p=son[p][u];
}
return cnt[p];
}
int main(){
int n;
cin>>n;
char op[2];
while (n -- ){
cin>>op>>str;
if(op[0]=='I'){
insert(str);
}else{
cout<<query(str)<<endl;;
}
}
return 0;
}
解法二:使用map
#include<iostream>
#include<map>
using namespace std;
map<string,int> m;
int main(){
int M;
char iq;
char s[20005];
cin>>M;
while(M--){
cin>>iq>>s;
if(iq=='I'){
m[s]++;
}
if(iq=='Q'){
bool ok=true;
map<string,int> :: iterator it;
for(it=m.begin();it!=m.end();it++)
{
if(it->first == s){
ok=false;
cout<<it->second<<endl;
}
}
if(ok)
cout<<0<<endl;
}
}
return 0;
}