字典树(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;
}

posted @ 2016-06-15 22:59  Krew  阅读(162)  评论(0)    收藏  举报