学习NOTE 7——字典树(Trie)
之前忘写了。。。赶紧补一篇。我是煞笔。
是什么
字典树(trie),可以理解为把字典建成树。 为什么这么说?(我哪知道)
如下:
app,add,bye
(1)
/ \
a b
/ \ \
p d y
/ \ \
p d e
欸,你别说,你还真别说,果然是“字典树”。
那字典树是用于干什么的?
给一下定义:字典树是一种用于高效存储和检索字符串集合的树形结构。
所以你知道了吧,就是用于储存与查找字符串的工具。
怎么实现
既然是“字典树”,那首先肯定要建树啊,就像很多树上的算法一样,都是树的嘛。
注意观察“是什么”里的例子,树是怎么建的。
- 用一个节点表示一个字符。
- 将前缀一样的字符串的前缀部分并在一起,剩下的部分作为子树。怪不得字典树也叫“前缀树”
那么我们需要记录什么?
眼睛向下看↓
// 定义常量:N为最大节点数(1e6+10),M为字符集大小(26个小写/大写字母)
const int N=1e6+10,M=26;
//trie[N][M]:字典树(Trie)的存储数组,trie[p][c]表示从节点p通过字符c到达的子节点编号
//cnt[p]:记录以节点p结尾的单词个数(即该字符串被插入的次数)
//tot:节点总数,用于给新节点分配编号(根节点编号为0)
int trie[N][M],cnt[N],tot;
这样就记住了节点、字符串、序号。
那建树...
//插入单词到Trie树
void insert(string s){
int p=0;//从根节点开始
for(auto c:s){
int ch=c-'a';
if(!trie[p][ch]) trie[p][ch]=++tot;//若该分支不存在则新建节点
p=trie[p][ch];//沿分支向下走
}
cnt[p]++;//单词末尾节点计数+1,表示该单词存在且插入次数
}
有了树,别忘了我们要干什么 (别忘本),回顾一下定义:字典树是一种用于高效存储和检索字符串集合的树形结构。
储存了,就该检索了。那又怎么查呢?
根据字典树的特性,我们目前可以查这些东西:查询单词s是否在Trie树中(查单词)、是否存在以s为前缀的单词(查前缀)。
转化一下:要查的字符串是否和Trie树的一个链组成的字符串、是否存在一个链的一部分和要查的单词/前缀相等。
查询的方式和建树是一样的,那么这就很好写了。
查单词:
//查询单词s是否在Trie树中
bool search(string s){
int p=0;
for(auto c:s){
int ch=c-'a';
if(!trie[p][ch]) return 0;//路径中断,单词不存在
p=trie[p][ch];
}
return cnt[p]>0;//返回该单词是否被插入过(cnt[p]>0表示存在)
}
查前缀:
//判断是否存在以s为前缀的单词
bool startwith(string s){
int p=0;
for(auto c:s){
int ch=c-'a';
if(!trie[p][ch]) return 0;//路径中断,无此前缀
p=trie[p][ch];
}
return 1;//能走完路径,说明有单词以s为前缀
}
这些就是字典树的基本功能啦~
栗子:
PID:P2580
Ber,为什么不能直接不让他玩炉石?
思路:版字题,考虑先把所有人的名字建一个字典树,每次查找时用map统计一下次数就ok了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
//Trie树节点数组:trie[p][c]表示节点p通过字符c(0-25)到达的子节点编号,0表示不存在
const int N=1e6+10,M=26;
int trie[N][M],cnt[N],tot;
map<string,int> mp;
//插入单词s到Trie树
void insert(string s){
int p=0;//从根节点开始
for(auto c:s){
int ch=c-'a';
if(!trie[p][ch]) trie[p][ch]=++tot;//若该分支不存在则新建节点
p=trie[p][ch];//沿分支向下走
}
cnt[p]++;//单词末尾节点计数+1,表示该单词存在且插入次数
}
//查询单词s是否在Trie树中
bool search(string s){
int p=0;
for(auto c:s){
int ch=c-'a';
if(!trie[p][ch]) return 0;//路径中断,单词不存在
p=trie[p][ch];
}
mp[s]++;//记录该单词被查询的次数
return cnt[p]>0;//返回该单词是否被插入过(cnt[p]>0表示存在)
}
int n,q;
string s;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>s;
insert(s);//添加名字
}
cin>>q;
while(q--){
cin>>s;
bool x=search(s);
if(!x) cout<<"WRONG\n";//名字不存在
else if(mp[s]>1) cout<<"REPEAT\n";//名字存在但重复查询
else cout<<"OK\n";//名字存在且首次查询
}
return 0;
}

浙公网安备 33010602011771号