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

ybt 1471

第一种解法

#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

ybt1474

第一种解法

#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

luogu P2580

第一种解法

# 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

luogu p8306

第一种解法

#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

luogu p2922

第一种解法

#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';
    }
} 
posted @ 2024-01-06 15:29  一个追寻光的人  阅读(42)  评论(0)    收藏  举报