什么是字典树
上图来自luogu题解
这是一种字典树,不过本文讲的不是这种图,本文要讲一种更通俗易懂的(博主个人观点)
我要讲的是每个节点只存一个字母或数字,通过打标记的方法实现find的
像这样
上图来自百度百科
如何存储字典树
我不想写那些很难搞的指针,虽然用指针会使程序简单明了,可能以后会更新上指针版的吧,咕咕咕
个人认为存储字符串只需要按顺序就可以了,这应该也是字典树的精髓
如要顺序存储下面的字符串:"lyqalioi","orzliuzitong","zbqisredsun","orztzt".......
只需用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;//更新标记
}
如何查找字符串有没有出现
如果彻底懂了上面这里就不用看了
这里就不解释了和上面一样
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;
}
第一个图的那种线段树
代码比较复杂,不过效率比上文的高很多,所以本文也会讲解
应用
大多数字典树的题目都不会直接让你搞一棵树就完通常会带有一些附加条件,会在下面的例题中有涉及
例题
1.统计难题
就是统计了个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]==' ') break;
add(s);
}
while(scanf("%s",s)!=EOF) {
cout<<find(s)<<'
';
}
return 0;
}