[noj1393] 哦妈咪妈咪吼

题目链接:http://acm.nbut.cn/Problem/view.xhtml?id=1393


类型:字典树

字典树小普及:

Trie树也叫字典树,是一种用于快速检索的多叉树结构。如英文字母的字典树是一个26叉树。数字的字典树是一个10叉树。Trie树把要查找的关键词看作一个字符序列,并根据构成关键词字符的先后顺序构造用于检索的树结构;一棵m度的Trie树或者为空,或者由mm度的Trie树构成。特别的:和二叉查找树不同,在Trie树中,每个结点上并非存储一个元素。在Trie树中查找一个关键字的时间和树中包含的结点数无关,而取决于组成关键字的字符数。

特点:

 

①利用串的公共前缀->节约内存。

②根结点(root)不包含任何字母。

③其余结点仅包含一个字母(非元素)

④每个结点的子结点包含字母不同。

 

查找过程:

①在Trie树上进行检索总是始于根结点。

②取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索。

③在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。

④在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。

 

字典树比较一般的实现是用指针,所以又可分为动态开辟内存的字典树和静态开辟内存的字典树。两种写法各有长处,一般静态的速度较快,而动态的代码较简。



原来做字典树的时候一直在用动态内存分配,做这题的时候一直时间很长,甚至比不过他们暴力的算法。。。
后来才知道,原来动态内存分配要花很多时间的,至于为什么。。我也不知道,等知道了再补上。。

题目大意:
老蔡被网走了,然后为了脱离魔法牢笼,开始进行奇葩的字符匹配游戏。好吧。。其实题意就是先输入一系列的字符串,然后进行查询操作,用输入的前缀去匹配之前输入的字符串(单词本身也是自己的前缀).然后输出匹配的个数。
比如第一组数据
3
ab
abc
a
3
a
ab
abc
输入的字符串有ab,abc,a;然后匹配a的时候有三个;匹配ab有两个即ab,abc;匹配abc有一个即abc本身;

题解:
这题其实就是字典树的裸题,没什么好说的。。

1.字典树的结构体

struct Node
{
int sum;//记录多少单词拥有以该节点为末尾的前缀
Node *lt[26];//后续节点, 其中26是表示每层有多少种类的数,可根据题意自行更改
}a[500100];//开辟静态内存
Node *head;//头节点


2.插入操作

void insert(char str[]) //插入的字符串下标。。
{
int i,j;
Node *t,*s=head; //将s指向头节点
int len=strlen(str); //计算插入字符串的长度
for (i=0;i<len;i++)
{
int id=str[i]-'a'; //将字符转换成0-26的形式给id,如'a'-'a'=0
if (s->lt[id]==NULL) //如果s的相应子节点为空,则从s的br[id]节点建树
{
t=&a[time++]; //使用已开辟的静态,并将指向静态内存的指针往下移
for (j=0;j<26;j++)
{
t->lt[j]=NULL;
}
t->sum=0;
s->lt[id]=t;
}
s=s->lt[id];
s->sum++;
}
}

3.查询操作

int query(char str[])
{
int i,sum=0;
Node *s=head;
int len=strlen(str);
for (i=0;i<len;i++)
{
int id=str[i]-'a';
if (s->lt[id]==NULL) //如果查询的字符不存在,直接返回0
{
return 0;
}
else
{
s=s->lt[id]; //将指针指向下一个字符
sum=s->sum; //改变sum的值
}
}
return sum;
}



完整代码:(静态内存开辟,时间已比动态的快20倍。。)

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Node
{
int sum;
Node *lt[26];
}a[500100];

Node *head;
int time=0;

void insert(char str[])
{
int i,j;
Node *t,*s=head;
int len=strlen(str);
for (i=0;i<len;i++)
{
int id=str[i]-'a';
if (s->lt[id]==NULL)
{
t=&a[time++];
for (j=0;j<26;j++)
{
t->lt[j]=NULL;
}
t->sum=0;
s->lt[id]=t;
}
s=s->lt[id];
s->sum++;
}
}

int query(char str[])
{
int i,sum=0;
Node *s=head;
int len=strlen(str);
for (i=0;i<len;i++)
{
int id=str[i]-'a';
if (s->lt[id]==NULL)
{
return 0;
}
else
{
s=s->lt[id];
sum=s->sum;
}
}
return sum;
}

int main()
{
int n,m,i;
while (~scanf("%d",&n))
{
time=0;
head=&a[time++];
for (i=0;i<26;i++)
{
head->lt[i]=NULL;
head->sum=0;
}
char str[20];
getchar();
for (i=0;i<n;i++)
{
scanf("%s",str);
insert(str);
}
scanf("%d",&m);
getchar();
for (i=0;i<m;i++)
{
scanf("%s",str);
printf("%d\n",query(str));
}
}
return 0;
}









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值