字典树 HDU1251 HDU 5687

这篇博客介绍了如何使用C++实现Trie树,包括插入单词、删除以特定字符串为前缀的单词、查询单词是否存在以及查询以特定字符串为前缀的单词数量。文章提供了详细的代码实现,并附带了两个示例,帮助理解Trie树在字符串处理中的应用。
摘要由CSDN通过智能技术生成

模板解决了五个问题,插入,删除,查询是否存在这个单词,查询以这个字符串为前缀的单词数量,查询这个单词是否有前缀

https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/trie-tree-de-shi-xian-gua-he-chu-xue-zhe-by-huwt/

https://blog.csdn.net/qq_43906740/article/details/96474307?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0.essearch_pc_relevant&spm=1001.2101.3001.4242

放两篇解析,这里只放模板,因为我自己觉得这两篇解析该说的都说了,我暂时没有新的领悟

下面可以ACHDU1251

#include<cstdio>//https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/trie-tree-de-shi-xian-gua-he-chu-xue-zhe-by-huwt/
#include<iostream>//https://blog.csdn.net/qq_43906740/article/details/96474307?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0.essearch_pc_relevant&spm=1001.2101.3001.4242
#include<string>
#include<cstring>
using namespace std;
const int N=1e7+10;
struct node {
    int isEnd;//true表示这是个尾节点,从根节点找到这里是一个单词
    int prefix;//以这个字符串为前缀的单词数 如果该节点是根节点,代表单词数
    int next[27];//子节点地址 数组下标是字符映射 从0开始 假设最小的字符是'x',那么每个字符的值为'y'-'x'
} trie[N];
int cnt;//cnt为trie的下标
string read(){
    char ch=getchar();string a="";
    while(ch!='\n'){
        a+=ch;
        ch=getchar();
    }
    return a;
}
inline void init() {
    for(int i=0; i<=cnt; i++) {
        trie[i].isEnd=0;
        trie[i].prefix=0;
        memset(trie[i].next,0,sizeof(trie[i].next));
    }
    cnt=0;//0是根节点
}
inline void insert(string word) { //插入单词
    int len=word.size();
    int now=0;//now代表当前节点在树中的下标
    for(int i=0; i<len; i++) {
        int ch=word[i]-'a';//字符映射
        if(!trie[now].next[ch]) { //如果这个字符没被开辟
            trie[now].next[ch]=++cnt;
        }//如果开辟就不用再开辟了,直接用即可
        trie[now].prefix++;//更新前缀数
        now=trie[now].next[ch];//更新地址
    }
    trie[now].prefix++;//更新前缀数
    trie[now].isEnd=true;//这是尾节点
}
inline bool searchExit(string word) { //查询是否存在这个单词
    int now=0,len=word.size();
    for(int i=0; i<len; i++) {
        int ch=word[i]-'a';
        if(!trie[now].next[ch]) { //没这条边,所以不存在这个字符串
            return false;
        }
        now=trie[now].next[ch];
    }
    return trie[now].isEnd;//返回是否有字符串在该点结束
}
inline int searchPrefix(string word) { //查询以这个字符串为前缀的单词数量
    int now=0,len=word.size();
    for(int i=0; i<len; i++) {
        int ch=word[i]-'a';
        if(!trie[now].next[ch]) { //没这条边,所以不存在这个字符串,更不存在以其为前缀的单词
            return false;
        }
        now=trie[now].next[ch];;
    }
    return trie[now].prefix;//查询前缀数
}
inline bool searchExitPrefix(string word) { //查询这个单词是否有前缀
    int now=0,len=word.size();
    for(int i=0; i<len; i++) {
        if(trie[now].isEnd) return true;//如果有点在遍历字符串word的路径上且这个点是尾点,那么已经遍历到的字符串就是这个字符串的前驱
        int ch=word[i]-'a';
        if(!trie[now].next[ch]) { //之前的边不是前缀,又没这条边,所以不存在前缀 
            return false;
        }
        now=trie[now].next[ch];;
    }
    return trie[now].isEnd;//相同也算存在前缀(根据题意更改) 
}
void Delete(string word) { //删除所有前缀等于给定字符串的单词
	int now=0,len=word.size();
	for(int i=0; i<len; i++) {
		int ch=word[i]-'a';
		if(!trie[now].next[ch]) { //没这条边,所以不存在这个字符串,更不存在以其为前缀的单词
			return ;
		}
		now=trie[now].next[ch];
	}
	trie[now].isEnd=false;
	memset(trie[now].next,0,sizeof(trie[now].next));
	int t=trie[now].prefix;
	trie[now].prefix=0;
	now=0;
	for(int i=0; i<len; i++) {
		int ch=word[i]-'a';
		trie[now].prefix-=t;
		now=trie[now].next[ch];
	}
}
int main() {
    init();
    ios::sync_with_stdio(false);
    string a;
    while(1){
        a=read();
        if(a.empty())    break;
        insert(a);
    } 
    while(cin>>a){
        int t=searchPrefix(a);
        printf("%d\n",t);
    }
    return 0;
}

下面可以ACHDU5687

#include<cstdio>//https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/trie-tree-de-shi-xian-gua-he-chu-xue-zhe-by-huwt/
#include<iostream>//https://blog.csdn.net/qq_43906740/article/details/96474307?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0.essearch_pc_relevant&spm=1001.2101.3001.4242
#include<string>
#include<cstring>
using namespace std;
const int N=1e7;
struct node {
	int isEnd;//true表示这是个尾节点,从根节点找到这里是一个单词
	int prefix;//以这个字符串为前缀的单词数 如果该节点是根节点,代表单词数
	int next[27];//子节点地址 数组下标是字符映射 从0开始 假设最小的字符是'x',那么每个字符的值为'y'-'x'
} trie[N];
int cnt;//cnt为trie的下标
string read() {
	char ch=getchar();
	string a="";
	while(ch!='\n') {
		a+=ch;
		ch=getchar();
	}
	return a;
}
inline void init() {
	for(int i=0; i<=cnt; i++) {
		trie[i].isEnd=0;
		trie[i].prefix=0;
		memset(trie[i].next,0,sizeof(trie[i].next));
	}
	cnt=0;//0是根节点
}
inline void insert(string word) { //插入单词
	int len=word.size();
	int now=0;//now代表当前节点在树中的下标
	for(int i=0; i<len; i++) {
		int ch=word[i]-'a';//字符映射
		if(!trie[now].next[ch]) { //如果这个字符没被开辟
			trie[now].next[ch]=++cnt;
		}//如果开辟就不用再开辟了,直接用即可
		trie[now].prefix++;//更新前缀数
		now=trie[now].next[ch];//更新地址
	}
	trie[now].prefix++;//更新前缀数
	trie[now].isEnd=true;//这是尾节点
}
inline int searchPrefix(string word) { //查询以这个字符串为前缀的单词数量
	int now=0,len=word.size();
	if(len<=0)	return 0;
	for(int i=0; i<len; i++) {
		int ch=word[i]-'a';
		if(!trie[now].next[ch]) { //没这条边,所以不存在这个字符串,更不存在以其为前缀的单词
			return false;
		}

		now=trie[now].next[ch];;
	}
	return trie[now].prefix;//查询前缀数
}
void Delete(string word) { //删除所有前缀等于给定字符串的单词
	int now=0,len=word.size();
	for(int i=0; i<len; i++) {
		int ch=word[i]-'a';
		if(!trie[now].next[ch]) { //没这条边,所以不存在这个字符串,更不存在以其为前缀的单词
			return ;
		}
		now=trie[now].next[ch];
	}
	trie[now].isEnd=false;
	memset(trie[now].next,0,sizeof(trie[now].next));
	int t=trie[now].prefix;
	trie[now].prefix=0;
	now=0;
	for(int i=0; i<len; i++) {
		int ch=word[i]-'a';
		trie[now].prefix-=t;
		now=trie[now].next[ch];
	}
}
int main() {
	int N;
	scanf("%d",&N);
	string a,b;
	int t;
	while(N--) {
		cin>>a>>b;
		if(a=="insert")	insert(b);
		else if(a=="delete") Delete(b);
		else if(a=="search") {
			if(searchPrefix(b))	cout<<"Yes"<<endl;
			else cout<<"No"<<endl;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值