第40期:数据结构-字符串-字典树

本文详细介绍了字典树(Trie Tree)的数据结构及其在字符串搜索和插入中的实现,包括字典树节点的定义、操作方法以及字典树的整体构造。通过实例展示了字典树如何用于解决POJ3630PhoneList和P2580点名问题等字符串匹配问题,强调了其在字符串处理中的高效性。
摘要由CSDN通过智能技术生成

参考:

夜深人静写算法(七)- 字典树


Code_Templates/字典树 字母模板.cpp at master · WhereIsHeroFrom/Code_Templates (github.com)



夜深人静写算法(七)- 字典树

 1.字典树实现

a.字典树结点

字典树结点包括三个信息:结尾标记、前缀数量、子结点列表,定义如下:

#include<bits/stdc++.h>
using namespace std;
const int trie_node_count=26;
const int trie_node_null=-1;

typedef const char ValueType;
typedef int TrieData;

int trieNodeValueHash(ValueType v) {         // 修改点 6
    return v - 'a';
}
//字典树结点
struct TrieNode{
	bool isword;//结点标记,判断是否是1个完整字符串
	//当插入一个完整的字符串后,在对应的结点的这个标记置上true
	int num;//代表了整个字典中有多少个以s为前缀的字符串
	//令从根结点到这个结点的路径上的字母组成的字符串为s
	int nodes[trie_node_count];//一个连续的数组,用来存储结点的每个子结点的编号
	//也可以认为是指向子结点的指针
	
	inline void setNode(int nodeIdx,int node){
		nodes[nodeIdx]=node;
	}
	inline int getNode(int nodeIdx){
		return nodes[nodeIdx];
	}
	inline bool hasNode(int nodeIdx){
		//结点范围判断
		if(nodeIdx<0||nodeIdx>=trie_node_count){
			return false;
		} 
		return nodes[nodeIdx]!=trie_node_null;
	}
	inline void resetData(){
		num=0;
		isword=false;
	}
	//提供一个初始化函数reset(),将结点默认到初始化状态
	inline void reset(){
		resetData();
		memset(nodes,trie_node_null,sizeof(nodes));
	}
	inline void addNum(int d){
		num+=d;
	}
	inline int getNum(){
		return num;
	}
	inline void setTrieData(TrieData v){
		;
	}
	inline TrieData getTrieData(){
		;
	}
	inline void setWord(bool isw){
		isword=isw;
	}
	inline bool isWord(){
		return isword;
	}
}; 

const int trie_word_count=400000;
const int trie_word_length=10;
const int trie_node_caches=trie_word_count*trie_word_length;

//字典树
struct TrieTree{
	int nodeId;//nodeId则代表了目前已经用掉的结点数,每次调用genNode()返回一个已经初始化好的字典树结点 
	int root;
	TrieNode *nodes;//nodes指针存储了所有将要用到的字典树结点的内存首地址,在构造的时候在堆上申请空间
	void initialize(){
		nodeId=0;
		root=genNode();
	}
	
	TrieNode *root() const { return node(root); }
	TrieNode *node(int idx) const { return &(nodes[idx]); }
	
	int genNode(){
		TrieNode *pkNode=&(nodes[nodeId]);
		pkNode->reset();
		return nodeId++;
	}
	bool hasNode(TrieNode *pkNow,int nodeIdx){
		if(pkNow->hasNode(nodeIdx)){
			TrieNode *pkNode=node(pkNow->getNode(nodeIdx));
			if(pkNode->getNum()>0){
				return true;
			}
		}
		return false;
	}
	void checkNode(TrieNode *pkNow,int nodeIdx){
		if(!pkNow->hasNode(nodeIdx)){
			pkNow->setNode(nodeIdx,genNode());
		}
	}
	inline void construct(){
		root=0;
		nodeId=0;
		nodes=new TrieNode[trie_node_caches];
	}
	void destruct(){
		if(nodes){
			delete[] nodes;
		}
	}
	void insert_word(int vSize,ValueType v[],TrieData data){
		//插入字符串到字典树 
		TrieNode *pkNow=root();
		for(int i=0;i<vSize;++i){
			int nodeIdx=trieNodeValueHash(v[i]);
			checkNode(pkNow,nodeIdx);
			pkNow=node(pkNow->getNode(nodeIdx));
			pkNow->addNum(1);
		}
		pkNow->setWord(true);
		pkNow->setTrieData(data);
	}
	bool query_word(int vSize,ValueType v[]){
		//查找对应字典中,是否有字符串等于v[]
		TrieNode *pkNow=root();
		for(int i=0;i<vSize;++i){
			int nodeIdx=trieNodeValueHash(v[i]);
			if(!hasNode(pkNow,nodeIdx)){
				return false;
			}
			pkNow=node(pkNow->getNode(nodeIdx));
		} 
		return pkNow->isWord();
	}
}; 
int main(){
	TrieTree T;
	T.construct();
	cout<<T.nodeId<<endl;
	return 0;
}

例题

1. POJ3630 Phone List 

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
const int maxn=1e4+5;
const int maxm=1e5+5;
int T,n;
int ch[maxm][10];
bool word[maxm];
char str[maxn];
int tot;
bool insert(char *s){//插入新字符串
	int len=strlen(s);
	int rt=0;
	bool flag=false;
	for(int i=0;i<len;++i){
		int c=s[i]-'0';
		if(!ch[rt][c]){
			ch[rt][c]=++tot;
			flag=true;
		}
		rt=ch[rt][c];
		if(word[rt]){//判断是否是其它串的结尾标记
			return false; 
		}
	} 
	word[rt]=true;
	return flag;
}
void init(){//初始化
	tot=0;
	memset(word,false,sizeof word);
	memset(ch,0,sizeof ch); 
}
int main(){
	cin>>T;
	while(T--){
		init();
		cin>>n;
		bool flag=false;
		for(int i=1;i<=n;++i){
			cin>>str;
			if(flag) continue;
			if(!insert(str)){
				flag=true;
				continue;
			}
		}
		if(!flag) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0; 
}

2. P2580 于是他错误的点名开始了

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;

struct Trie{
	int val[N],ch[N][26],sz;
	Trie(){
		sz=1;
		memset(ch[0],0,sizeof ch[0]);
		memset(val,0,sizeof val);
	}//初始化
	int idx(char c){ return c-'a'; }
	void insert(char *s){
		int u=0,len=strlen(s+1);
		for(int i=1;i<=len;++i){
			int c=idx(s[i]);
			if(!ch[u][c]){
				memset(ch[sz],0,sizeof ch[sz]);
				ch[u][c]=sz++;
			}
			u=ch[u][c];
		}
	}
	int search(char *s){
		int u=0,len=strlen(s+1);
		for(int i=1;i<=len;++i){
			int c=idx(s[i]);
			if(!ch[u][c]) return 0;
			//如果没有这个节点,也就意味着询问的这个字符串不存在,输出WRONG
			u=ch[u][c];
		}
		if(!val[u]){
			val[u]=1;
			return 1;//这个字符串已经询问过了,标识一下,然后输出OK
		}
		return 2;//这个字符串正确,但不是第一次出现,输出REPEAT 
	} 
}tree;
int n,m;
char s[N];
int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>(s+1);
		tree.insert(s);
	}
	cin>>m;
	for(int i=1;i<=m;++i){
		cin>>(s+1);
		int pd=tree.search(s);
		if(pd==0) cout<<"WRONG\n";
		else if(pd==1) cout<<"OK\n";
		else if(pd==2) cout<<"REPEAT\n";
	}
	return 0;
}

3. P5149 会议座位

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值