字典树的使用

字典树Trie是一个很简单的数据结构,直接上代码:

const int MAXN = 500005;
int next[MAXN][26], cnt; // 用类似链式前向星的方式存图,next[i][c]表示i号点所连、存储字符为c+'a'的点的编号
void init() // 初始化
{
    memset(next, 0, sizeof(next)); // 全部重置为0,表示当前点没有存储字符
    cnt = 1;
}
void insert(const string &s) // 插入字符串
{
    int cur = 1;
    for (auto c : s)
    {
        // 尽可能重用之前的路径,如果做不到则新建节点
        if (!next[cur][c - 'a']) 
            next[cur][c - 'a'] = ++cnt; 
        cur = next[cur][c - 'a']; // 继续向下
    }
}

这里使用const  string&s比string s更有效率,&是引用指针传递,const 表示只读 加快了读取速度

通过字典树查找字符串用叶子代表整体,来确认是否存在,或者最基础的用法查找某个前缀是否出现过,如下所示

bool find_prefix(const string &s) // 查找某个前缀是否出现过
{
    int cur = 1;
    for (auto c : s)
    {
        // 沿着前缀所决定的路径往下走,如果中途发现某个节点不存在,说明前缀不存在
        if (!next[cur][c - 'a'])
            return false;
        cur = next[cur][c - 'a'];
    }
    return true;
}

洛谷P2580 于是他错误的点名开始了

第一行一个整数 n,表示班上人数。
接下来n 行,每行一个字符串表示其名字(互不相同,且只含小写字母,长度不超过 50)。
第 n+2 行一个整数  m,表示教练报的名字个数。
接下来 m行,每行一个字符串表示教练报的名字(只含小写字母,且长度不超过 50 )。
输出格式
对于每个教练报的名字,输出一行。
如果该名字正确且是第一次出现,输出 OK,如果该名字错误,输出 WRONG,如果该名字正确但不是第一次出现,输出 REPEAT。

这题用哈希表能很快解决,这里是字典树的解法,判断重复再加一个vis数组即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<set>
#include<string.h>
#include<vector>
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define ll long long 
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=5e5+5;
namespace trie
{
int next[maxn][26];
bool vis[maxn],exist[maxn];
int cnt;
void init(){
	memset(next,0,sizeof(next));
	cnt=1;
} 
void insert(const string &s){
	int cur=1;
	for(auto c:s){
		if(!next[cur][c-'a']){
			next[cur][c-'a']=++cnt;
		}
		cur=next[cur][c-'a'];
	}
	exist[cur]=true;
}
int  find(const string &s){
	int cur=1,ans;
	for(auto c:s){
		if(!next[cur][c-'a'])return 0;
		cur=next[cur][c-'a'];
	}
	if(!exist[cur])ans=0;
	else if(!vis[cur])ans=1;
	else ans=2;
	vis[cur]=true;
	return ans;
}	
 } 

int main(){
	int n;
	cin>>n;
    string s;
	while(n--){
		cin>>s;
		trie::insert(s); 
	}
	int m;
	cin>>m;
	while(m--){
		cin>>s;
		switch(trie::find(s)){
		case 0: 
			cout<<"WRONG"<<endl;
			break;
		case 1:
			cout<<"OK"<<endl;
			break;
		case 2:
			cout<<"REPEAT"<<endl; 
		}
	}
	return 0;
}

讲下这里namespace的用法:避免命名重复c++11采用了命名空间namespace来命名,在空间内的 变量只属于内部,namespace Trie作为字典树的专属命名空间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值