上图来自luogu题解ios
这是一种字典树,不过本文讲的不是这种图,本文要讲一种更通俗易懂的(博主我的观点)spa
我要讲的是每一个节点只存一个字母或数字,经过打标记的方法实现find的指针
像这样
上图来自百度百科code
我不想写那些很难搞的指针,虽然用指针会使程序简单明了,可能之后会更新上指针版的吧,咕咕咕blog
我的认为存储字符串只须要按顺序就能够了,这应该也是字典树的精髓字符串
如要顺序存储下面的字符串:"lyqalioi","orzliuzitong","zbqisredsun","orztzt".......get
只需用tree[i][j]来表示以i为根的j号孩子的编号(仔细搞搞这里,比较难懂)
用flag[i]来表示以i号节点为结束的字符串有没有(前面的描述不太准确,就是到i号节点的字符串有没有)
而后就能够迭代根编号root了博客
void add(char *s) { int len=strlen(s),root=0/*root为根节点,初始化0*/,id/*id是编号用char-'a'表示*/; for(int i=0;i<len;++i) { id=s[i]-'a'; if(!tree[root][id]) tree[root][id]=++sum; root=tree[root][id];//更新根节点,以前在博客里写了 } flag[root]=true;//更新标记 }
若是完全懂了上面这里就不用看了string
这里就不解释了和上面同样
bool find(char *s) { int len=strlen(s),root=0/*root为根节点,初始化0*/,id/*id是编号用char-'a'表示*/; for(int i=0;i<len;++i) { id=s[i]-'a'; if(!tree[root][id]) return false; root=tree[root][id]; } if(flag[root]==true) return true;//看有没有出现以root结束的字符串 else return false; }
代码比较复杂,不过效率比上文的高不少,因此本文也会讲解
大多数字典树的题目都不会直接让你搞一棵树就完一般会带有一些附加条件,会在下面的例题中有涉及
就是统计了个sum[root]没什么好说的
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #include<stack> #include<vector> #include<map> #include<string> #include<cstring> #define int long long int using namespace std; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } const int MAXN=2e6+5; int tree[MAXN][30],tot,sum[MAXN]; void add(char *s) { int root=0,id,len=strlen(s); for(int i=0; i<len; ++i) { id=s[i]-'a'; if(!tree[root][id]) tree[root][id]=++tot; sum[tree[root][id]]++; root=tree[root][id]; } } int find(char *s) { int root=0,id,len=strlen(s); for(int i=0; i<len; ++i) { id=s[i]-'a'; if(!tree[root][id]) return 0; root=tree[root][id]; } return sum[root]; } char s[MAXN]; signed main() { while(gets(s)) { if(s[0]=='\0') break; add(s); } while(scanf("%s",s)!=EOF) { cout<<find(s)<<'\n'; } return 0; }