【数据结构】字典树TrieTree图文详解_Avalon Demerzel的博客-CSDN博客
建议先看一看上面那两个网站,不透彻的可以再看我的理解一下。
由于字典树的每一个节点都是有用的,都有可能是一个单词的结尾,所以需要给每个节点打上编号,所以字典树所占的内存会非常大。
第一维大小可以直接用字符串最长长度来测试(不够再扩大)
最重要的是二维数组的含义
根据打上编号的字典树结构我们可以发现,每一个节点下面都可以跟26种情况,为了方便迭代查阅,定义二维数组为 t[当前节点编号][连接的字母]=子节点编号
即有 t[子节点编号][连接的字母]=子节点编号的编号,直接套用自己即可。
扫描二维码关注公众号,回复:
14671121 查看本文章
如果对应有编号,那就说明连上了,没有编号,那就是没连着。
这道题有大写字母,小写字母,数字,一共26+26+10=62种。
将其映射到二维数组中。
int getNum(char c){
if(c>='a' and c<='z')return c-'a';
else if(c>='A' and c<='Z')return c-'A'+26;
else return c-'0'+52;
}
根据二维数组含义实现插入。
inline void insert(){
int now=0;//从根节点开始往下连接
for(int i=0;i<s.length();++i){
int k= getNum(s[i]);//得到映射
if(!t[now][k])t[now][k]=++idx;//没有这个字母的话,新建一个节点
now=t[now][k];//往下搜
cnt[now]++;//记录所有前缀,此题要用
}
}
和插入基本一样的查找。
int find(){
int now=0;
for(int i=0;i<s.length();++i){
int k= getNum(s[i]);
if(!t[now][k])return 0;
now=t[now][k];
}
return cnt[now];
}
最后,由于初始化一直在用memset一直导致TLE。
原因应该是需要被初始化的空间(编号范围)远小于N,如果用memset去初始化所有空间就会超时。
所以这道题的初始化应该用for来手动赋值(for的手动赋值和memset在汇编上实现一致,但是for的范围更小,所以此处速度比memset快的多)
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N=3e6+10;
inline void betterCinCout(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
}
int getNum(char c){
if(c>='a' and c<='z')return c-'a';
else if(c>='A' and c<='Z')return c-'A'+26;
else return c-'0'+52;
}
int T,n,q,t[N][63],idx,cnt[N];
string s;
inline void insert(){
int now=0;//从根节点开始往下连接
for(int i=0;i<s.length();++i){
int k= getNum(s[i]);//得到映射
if(!t[now][k])t[now][k]=++idx;//没有这个字母的话,新建一个节点
now=t[now][k];//往下搜
cnt[now]++;//记录所有前缀,此题要用
}
}
inline void init(){
for(int i=0;i<=idx;++i)
for(int j=0;j<=62;++j)
t[i][j]=0;
for(int i=0;i<=idx;++i)
cnt[i]=0;
idx=0;
}
int find(){
int now=0;
for(int i=0;i<s.length();++i){
int k= getNum(s[i]);
if(!t[now][k])return 0;
now=t[now][k];
}
return cnt[now];
}
int main(){
betterCinCout();
cin>>T;
while(T--){
init();
cin>>n>>q;
for(int i=1;i<=n;++i){
cin>>s;
insert();
}
for(int i=1;i<=q;++i){
cin>>s;
cout<<find()<<endl;
}
}
return 0;
}