参考:
夜深人静写算法(七)- 字典树
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;
}