Trie字典树学习笔记
Example
有如下单词
1.abacb 2.abc 3.acb 4.aaa 5.bcb
构建字典树如下图

template
#define N 3000005
#define M 70
int trie[N][M],index,cnt[N];
std::string s;
namespace Trie{
namespace Optimize_init{
void IO_Optimize(){
std::ios::sync_with_stdio(0);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
}
void init(){
for(int i=0;i<=index;i++)
for(int j=0;j<=62;j++)
trie[i][j]=0;
for(int i=0;i<=index;i++)cnt[i]=0;
index=0;
}
}
namespace build{
int getNumber(char ch){
if(ch>='a' and ch<='z')return ch-'a';
if(ch>='A' and ch<='Z')return ch-'A'+26;
else return ch-'0'+52;
}
void insert(){
int cur=0;
for(int i=0;i<s.size();i++){
int ch=getNumber(s[i]);
if(!trie[cur][ch])index++,trie[cur][ch]=index;
cur=trie[cur][ch],cnt[cur]++;
}
}
}
namespace query{
int find(){
int cur=0;
for(int i=0;i<s.size();i++){
int ch=Trie::build::getNumber(s[i]);
if(!trie[cur][ch])return 0;
cur=trie[cur][ch];
}
return cnt[cur];
}
}
}
例题1
第一种解法
#include<iostream>
#define ll long long
struct Node{
Node *son[10]={NULL};
//isend 是否为单词结尾
bool isend=false;
};
void buildtrie(Node *root,std::string s,bool &isPre){
Node *temp=root;
for(int i=0;i<s.size();i++){
int ch=s[i]-'0';//变为数字
if(temp->son[ch]==NULL){
Node *newNode=new Node;
//父结点指向孩子节点
temp->son[ch]=newNode;
}
//已经找到了答案
else if(i==s.size()-1){
isPre=true;
return;
}
//下一次从s[i]的节点去找
temp=temp->son[ch];
if(temp->isend){
isPre=true;
return;
}
}
//标记:已经到了终点
temp->isend=true;
}
int t,n;
std::string s;
int main(){
std::cin>>t;
Node *root;
while(t--){
root=new Node;
std::cin>>n;
// isPre 是否存在两个数字串 S,T,使得 S 是 T 的前缀
bool isPre=false;
while(n--){
std::cin>>s;
if(!isPre)buildtrie(root,s,isPre);
}
if(isPre)std::cout<<"NO\n";
else std::cout<<"YES\n";
}
}
提交上去会内存超限,这是因为每一次建字典树时,之前的所有信息全部都还有,故我们要写一个 deleteTrie 函数,来递归的删除。
#include<iostream>
#define ll long long
struct Node{
Node *son[10]={NULL};
//isend 是否为单词结尾
bool isend=false;
};
void buildtrie(Node *root,std::string s,bool &isPre){
Node *temp=root;
for(int i=0;i<s.size();i++){
int ch=s[i]-'0';//变为数字
if(temp->son[ch]==NULL){
Node *newNode=new Node;
//父结点指向孩子节点
temp->son[ch]=newNode;
}
//已经找到了答案
else if(i==s.size()-1){
isPre=true;
return;
}
//下一次从s[i]的节点去找
temp=temp->son[ch];
if(temp->isend){
isPre=true;
return;
}
}
//标记:已经到了终点
temp->isend=true;
}
void deleteTrie(Node *root){
if(!root)return;
for(int i=0;i<10;i++)
deleteTrie(root->son[i]);
delete root;
}
int t,n;
std::string s;
int main(){
std::cin>>t;
while(t--){
Node *root=new Node;
std::cin>>n;
// isPre 是否存在两个数字串 S,T,使得 S 是 T 的前缀
bool isPre=false;
while(n--){
std::cin>>s;
if(!isPre)buildtrie(root,s,isPre);
}
if(isPre)std::cout<<"NO\n";
else std::cout<<"YES\n";
deleteTrie(root);
}
}
这样就可以 AC 了。
第二种解法
用图来存Trie,记得多测清空。
# include<iostream>
# include<cstring>
# define N 100005 //行的长度
int trie[N][10],id=1,isEnd[N];
//isEnd 是否到单词结尾,下表是ID
//buildtrie() 返回true表示找到前缀
// 返回false表示没有找到前缀
bool buildtrie(std::string s){
bool isPre=false;
int pointer=0;//表示当前指向到哪个节点的ID
for(int i=0;i<s.size();i++){
int ch=s[i]-'0';
if(trie[pointer][ch]==0){
//ch没有在pointer下面出现过,就造一个ch
trie[pointer][ch]=id,id++;
}
else if(i==s.size()-1){
isPre=true;
}
pointer=trie[pointer][ch];
if(isEnd[pointer]==1){
isPre=true;
}
}
//标记已经到单词结尾了
isEnd[pointer]=1;
return isPre;
}
void AllClear(){
memset(trie,0,sizeof(trie));
memset(isEnd,0,sizeof(isEnd));
}
int main(){
int t,n;
std::cin>>t;
while(t--){
AllClear();
id=1;
std::cin>>n;
bool isPre=false;
while(n--){
std::string s;
std::cin>>s;
if(buildtrie(s))isPre=true;
}
if(isPre)std::cout<<"NO\n";
else std::cout<<"YES\n";
}
}
例题2
第一种解法
#include<iostream>
#include<vector>
#define ll long long
std::vector<std::string>str;
struct Node{
Node *son[10]={NULL};
//isend 是否为单词结尾
bool isend=false;
};
void buildtrie(Node *root,std::string s,bool &isPre){
Node *temp=root;
for(int i=0;i<s.size();i++){
int ch=s[i]-'0';//变为数字
if(temp->son[ch]==NULL){
Node *newNode=new Node;
//父结点指向孩子节点
temp->son[ch]=newNode;
}
//已经找到了答案
else if(i==s.size()-1){
isPre=true;
return;
}
//下一次从s[i]的节点去找
temp=temp->son[ch];
if(temp->isend){
isPre=true;
return;
}
}
//标记:已经到了终点
temp->isend=true;
}
void deleteTrie(Node *root){
if(!root)return;
for(int i=0;i<10;i++)
deleteTrie(root->son[i]);
delete root;
}
int t=0,n;
std::string s;
int main(){
while(std::cin>>s){
if(s!="9"){
str.push_back(s);
n++;
}else{
bool isPre = false;
Node *root = new Node;
t++;
int idx=n;
while(n--){
if(!isPre)buildtrie(root, str[idx-1], isPre);
idx--;
}
if (isPre)std::cout << "Set " << t << " is not immediately decodable\n";
else std::cout<<"Set "<<t<<" is immediately decodable\n";
deleteTrie(root);
str.clear();
n=0;
}
}
}
例题3
第一种解法
# include<iostream>
struct Node{
char name;
Node *child[26]={nullptr};
bool isEnd=false;//是否到单词结尾
bool isCall=false;//是否被点过名
};
void buildTrie(Node*rootNode,std::string s){
Node *tempNode=rootNode;
for(int i=0;i<s.size();i++){
int ch=s[i]-'a';
if(tempNode->child[ch]==nullptr){
Node*newNode=new Node;
tempNode->child[ch]=newNode;
}
//从下一个节点开始找
tempNode=tempNode->child[ch];
}
//标记到姓名结尾
tempNode->isEnd=true;
}
void query(Node *rootNode,std::string s){
//1. Wrong -> 不是前缀 or 查询完毕,但是没有到单词结尾
//2. Repeat -> 查询到结尾了,但不是第一次
//3. Ok -> 查到结尾 and 没有被点名过
Node*tempNode=rootNode;
for(int i=0;i<s.size();i++){
int ch=s[i]-'a';
//如果不存在下一个孩子,输出Wrong
if(tempNode->child[ch]==nullptr){
std::cout<<"WRONG\n";
//没有再继续找的必要了
return;
}
tempNode=tempNode->child[ch];
}
//如果到达名字结尾
if(tempNode->isEnd){
//如果被点名了
if(tempNode->isCall==true){
std::cout<<"REPEAT\n";
return;
}else{
//如果没有被点名
std::cout<<"OK\n";
tempNode->isCall=true;
}
}else{
//如果没有到达名字结尾
std::cout<<"WRONG\n";
}
}
int main(){
int n;
std::cin>>n;
std::string s;
Node *rootNode=new Node;
for(int i=0;i<n;i++){
std::cin>>s;
buildTrie(rootNode,s);
}
int m;
std::cin>>m;
for(int i=1;i<=m;i++){
std::cin>>s;
query(rootNode,s);
}
}
第二种解法
#include<iostream>
#include<string>
#define N 1000005
#define M 60
int n,m,trie[N][M],index,cnt[N];
int isFirstVisit[N];
std::string s;
namespace Trie{
namespace Optimize_init{
void IO_Optimize(){
std::ios::sync_with_stdio(0);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
}
void init(){
for(int i=0;i<=index;i++)
for(int j=0;j<=53;j++)
trie[i][j]=0;
for(int i=0;i<=index;i++)cnt[i]=0;
index=0;
}
}
namespace build{
int getNumber(char ch){
if(ch>='a' and ch<='z')return ch-'a';
if(ch>='A' and ch<='Z')return ch-'A'+26;
else return ch-'0'+52;
}
void insert(){
int cur=0;
for(int i=0;i<s.size();i++){
int ch=getNumber(s[i]);
if(!trie[cur][ch])index++,trie[cur][ch]=index;
cur=trie[cur][ch];
}
cnt[cur]++;
}
}
namespace query{
void find(){
int cur=0;
for(int i=0;i<s.size();i++){
int ch=Trie::build::getNumber(s[i]);
if(!trie[cur][ch]){
std::cout<<"WRONG\n";
return;
}
cur=trie[cur][ch];
}
if(cnt[cur]>1){
std::cout<<"REPEAT\n";
return;
}
if(cnt[cur]==1){
cnt[cur]++;
std::cout<<"OK\n";
}
}
}
}
int main(){
Trie::Optimize_init::IO_Optimize();
std::cin>>n;
for(int i=1;i<=n;i++){
std::cin>>s;
Trie::build::insert();
}
std::cin>>m;
for(int i=1;i<=m;i++){
std::cin>>s;
Trie::query::find();
}
}
例题4
第一种解法
#include<iostream>
#include<string>
#define N 3000005
#define M 70
int t,n,q,trie[N][M],index,cnt[N];
std::string s;
namespace Trie{
namespace Optimize_init{
void IO_Optimize(){
std::ios::sync_with_stdio(0);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
}
void init(){
for(int i=0;i<=index;i++)
for(int j=0;j<=62;j++)
trie[i][j]=0;
for(int i=0;i<=index;i++)cnt[i]=0;
index=0;
}
}
namespace build{
int getNumber(char ch){
if(ch>='a' and ch<='z')return ch-'a';
if(ch>='A' and ch<='Z')return ch-'A'+26;
else return ch-'0'+52;
}
void insert(){
int cur=0;
for(int i=0;i<s.size();i++){
int ch=getNumber(s[i]);
if(!trie[cur][ch])index++,trie[cur][ch]=index;
cur=trie[cur][ch],cnt[cur]++;
}
}
}
namespace query{
int find(){
int cur=0;
for(int i=0;i<s.size();i++){
int ch=Trie::build::getNumber(s[i]);
if(!trie[cur][ch])return 0;
cur=trie[cur][ch];
}
return cnt[cur];
}
}
}
int main(){
Trie::Optimize_init::IO_Optimize();
std::cin>>t;
while(t--){
Trie::Optimize_init::init();
std::cin>>n>>q;
for(int i=1;i<=n;i++){
std::cin>>s;
Trie::build::insert();
}
for(int i=1;i<=q;i++){
std::cin>>s;
std::cout<<Trie::query::find()<<'\n';
}
}
}
第二种解法
#include<iostream>
#include<cstring>
#include<vector>
#define N 3000005
#define M 70
struct Node{
int child[M];
int cnt;
};
void IO_Optimize(){
std::ios::sync_with_stdio(0);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
}
void init(Node *rootNode,int &index){
memset(rootNode,0,sizeof(rootNode));
for(int i=0;i<=index;i++)
for(int j=0;j<=M;j++)
rootNode[i].child[j]=0;
for(int i=0;i<=index;i++)
rootNode[i].cnt=0;
index=0;
}
int getNumber(char ch){
if(ch>='a' and ch<='z')return ch-'a';
if(ch>='A' and ch<='Z')return ch-'A'+26;
else return ch-'0'+52;
}
void buildTrie(Node *rootNode,int &index,std::string s){
int cur=0;
for(auto i:s){
int ch=getNumber(i);
if(!rootNode[cur].child[ch]){
index++;
rootNode[cur].child[ch]=index;
}
cur=rootNode[cur].child[ch];
++rootNode[cur].cnt;
}
}
int query(Node *rootNode,std::string s){
int cur=0;
for(char i:s){
int ch=getNumber(i);
if(!rootNode[cur].child[ch]){
return 0;
}
cur=rootNode[cur].child[ch];
}
return rootNode[cur].cnt;
}
int main(){
int t,n,q;
std::cin>>t;
int tot=0;
while(t--){
std::string s;
std::cin>>n>>q;
Node rootNode[N];
int index=0;
init(rootNode,tot);
for(int i=1;i<=n;i++){
std::cin>>s;
buildTrie(rootNode,index,s);
}
// dfs(rootNode,index);
for(int i=1;i<=q;i++){
std::cin>>s;
std::cout<<query(rootNode,s)<<'\n';
}
tot=index;
}
}
例题5
第一种解法
#include<iostream>
#include<string>
#include<cmath>
#define N 3000005
#define M 2
int t,n,q,trie[N][M],idx,cnt[N],cnt2[N];
std::string s;
namespace Trie{
namespace Optimize_init{
void IO_Optimize(){
std::ios::sync_with_stdio(0);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
}
void init(){
for(int i=0;i<=idx;i++)
for(int j=0;j<=2;j++)
trie[i][j]=0;
for(int i=0;i<=idx;i++)cnt[i]=0;
idx=0;
}
}
namespace build{
int getNumber(char ch){
if(ch=='1')return 1;
else return 0;
}
void insert(){
int cur=0;
for(int i=0;i<s.size();i++){
int ch=Trie::build::getNumber(s[i]);
if(!trie[cur][ch])idx++,trie[cur][ch]=idx;
cur=trie[cur][ch];
cnt[cur]++;
}
cnt2[cur]++;
}
}
namespace query{
int find(){
int cur=0,res=0;
for(int i=0;i<s.size();i++){
int ch = Trie::build::getNumber(s[i]);
if(!trie[cur][ch]){
return res;
}
cur=trie[cur][ch];
res+=cnt2[cur];
}
return res+cnt[trie[cur][0]]+cnt[trie[cur][1]];
}
}
}
int main(){
Trie::Optimize_init::IO_Optimize();
int n,m;
std::cin>>n>>m;
for(int i=1;i<=n;i++){
s="";
int x;
std::cin>>x;
for(int j=1;j<=x;j++){
int p;
std::cin>>p;
s+=(char(p+48));
}
Trie::build::insert();
}
for(int i=1;i<=m;i++){
s="";
int x;
std::cin>>x;
for(int j=1;j<=x;j++){
int p;
std::cin>>p;
s+=(char(p+48));
}
std::cout<<Trie::query::find()<<'\n';
}
}

浙公网安备 33010602011771号