字典树(Trie)
字典树
在存储字符串集合时,朴素的存储不仅空间开销大,而且不便于查找。在字典树中,我们把所有字符串汇集成一棵树,用公共前缀的方法节省空间。
操作:
(1)插入:不断沿结点遍历,如无结点,则创立新节点。
(2)查找:同理,一直遍历即可。
代码(以HDU 1671为例):
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
using namespace std;
const int maxn=10000+10,max_node=100000+10,csize=10+2;
int T,n;
char s[maxn][csize];
int ch[max_node][csize];
int val[max_node];
int size;
void init() //初始化
{
size=0;
memset(ch[0],0,sizeof(ch[0]));
}
void insert(char *s) //插入
{
int u=0,len=strlen(s);
rep(i,0,len-1)
{
int k=(int)(s[i]-'0');
if (!ch[u][k])
{
memset(ch[++size],0,sizeof(ch[size]));
val[size]=0;
ch[u][k]=size;
}
u=ch[u][k];
}
val[u]=1;
}
bool count(char *s) //查找
{
int u=0,len=strlen(s);
rep(i,0,len-1)
{
int k=(int)(s[i]-'0');
u=ch[u][k];
}
rep(i,0,9) if (ch[u][i]) return 0;
return 1;
}
int main()
{
scanf("%d",&T);
while (T--)
{
int mark=1;
init();
scanf("%d",&n);
rep(i,1,n) {scanf("%s",s[i]);insert(s[i]);}
rep(i,1,n) if (!count(s[i])) {mark=0;break;}
if (mark) printf("YES\n"); else printf("NO\n");
}
return 0;
}
左儿子右兄弟表示法:
有时候trie里的结点太多了,而我们对每个结点都要开一个长为sigma_lenth的数组来储存,会造成大量浪费。
所以trie更多时候有左儿子右兄弟法来储存,原理如下:
对于一个结点,它的左结点表示它在原树中的儿子结点,它的右结点 表示它的右边的兄弟结点(不一定按照原树的顺序,要看插入顺序)
这样把多叉树转换为二叉树,少开了不必要的结点,具体见代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
using namespace std;
const int maxn=4000+10,maxm=4000000+10;
struct node{
int c,left,right; //结点字母值,左右结点编号
bool val; //是否为单词结点
void pre(int k) {c=k;left=0;right=0;val=0;}
} ch[maxm];
int n,size;
char s[maxn];
void init() //初始化
{
ch[0].pre(0);
size=0;
}
void insert(char*s)
{
int n=strlen(s),j=0;
rep(i,0,n-1)
{
int k=(int)(s[i]);
if (!ch[j].left) {ch[j].left=(++size);ch[ch[j].left].pre(k);} //没有儿子,新创结点
j=ch[j].left;
while (ch[j].c!=k) //向右搜索兄弟结点
{
if (!ch[j].right)
{
ch[j].right=(++size);
ch[ch[j].right].pre(k);
}
j=ch[j].right;
}
}
ch[j].val=true;
}
bool search(char*s)
{
int n=strlen(s),j=0;
rep(i,0,n-1)
{
int k=(int)(s[i]);
if (!ch[j].left) return false; else j=ch[j].left;
while (ch[j].c!=k)
{
if (!ch[j].right) return false;
j=ch[j].right;
}
}
if (ch[j].val) return true; else return false;
}
int main()
{
while (scanf("%d",&n)==1)
{
init();
rep(i,1,n) {scanf("%s",s);insert(s);}
rep(i,1,n) {scanf("%s",s);printf("%d\n",search(s));}
}
return 0;
}

浙公网安备 33010602011771号