Trie字典树
一、简介
将一个字符串用有根树的形式储存起来,树边对应着字符,树的节点储存着相关信息,边为转移,点为状态(官方简介)
二、基本性质
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符;
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
- 每个节点的所有子节点包含的字符都不相同。
三、操作实现
1.初始化:
仅含有一个根节点,该点的字符指针均指向空
2.插入:
当需要插入一个字符串S时,我们令一个指针起初指向根节点,然后扫描S中的字符
(1).若指针P的c字符指针指向一个已经存在的节点Q,则令P=Q
(2).若指针P的c字符指针指向空,则新建一个节点Q,则P的c字符指针指向Q,然后令P=Q
(3).当S中的字符串扫描完毕时,在当前节点指针P上标记它是一个字符串的结尾
3.查询:
当需查询一个字符串S在Trie中是否存在时,我们令一个指针P起初指向根节点,然后依次扫描S中的每一个字符c
(1).若指针P的c字符指针指向空,则说明S没有插入过Trie,查询结束
(2).若指针P的c字符指向一个已经存在的节点Q,则令P=Q
(3).当指针S中的字符扫描完毕,若当前节点P被标记为一个字符串的末尾,则说明Trie中已经存在,否则说明S中没有插入过Trie
四:具体代码(POJ3630)
#include<stdio.h> #include<stdlib.h> #include<string.h> #define FORa(i,s,e) for(int i=s;i<=e;i++) #define FORs(i,s,e) for(int i=s;i>=e;i--) #define gc pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),pa==pb)?EOF:*pa++ #define File(name) freopen(name".in","r",stdin);freopen(name".out","w",stdout); using namespace std; static char buf[100000],*pa=buf,*pb=buf; inline int read(); const int N=100,LEN=10; int T,n,tot,ch[N+1][LEN+1]; char s[LEN+1]; bool ans,end_p[N+1]; void Clear() { memset(ch,0,sizeof(ch)); memset(end_p,0,sizeof(end_p)); } bool Insert(char *fs) { int len=strlen(fs),u=1;//从根节点开始search bool flag=0; FORa(i,0,len-1) { int p=fs[i]-'0'; if(!ch[u][p]) ch[u][p]=++tot;//遍历儿子中是否有搜索串中此时的字符,没有的话,在树上新建一个节点储存下来 else if(i==len-1) flag=1;//如果字符串在Trie数中能够找到,返回存在 u=ch[u][p];//迭代 if(end_p[u]) flag=1;//看此时从根节点到现在节点是否存在完整单词,有的话,就完成了前缀的任务,将旗帜转为1 } end_p[u]=1;//标记从根节点到此节点产生了一个单词 return flag;//返回答案 } int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); ans=0,tot=1;//初始化答案,更新根节点 Clear();//清楚字典树除根节点的一切信息 FORa(i,1,n) { scanf("%s",s); if(Insert(s)) //插入字符串 { ans=1;//判断Trie是否有S这个字符串 break; } } ans?printf("NO"):printf("YES"); } return 0; } inline int read() { register int x(0);register int f(1);register c(gc); while(c<'0'||c>'9') f=c=='-'?-1:1,c=gc; while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc; return x*f; }
五、相关文章链接
https://pks-loving.blog.luogu.org/zi-fu-chuan-xue-xi-bi-ji-ha-xi-hash-yu-zi-dian-shu-trie
https://blog.csdn.net/jiutianhe/article/details/8076835