刚开始学习字典树,网上都是指针实现,而我个人比较喜欢数组实现,代码简洁,而且占内存少。
网上找到一个数组实现的模板,不过感觉有些问题,自己改了一下就用了。
由于之前没有怎么接触过数据结构,所以虽然字典树比较好理解,但是实现上对于我还是有点困难。在做题中,慢慢理解了字典树的数据结构。
之前只学习过C++的字符串处理,这道题输入的时候要用到strcmp函数,我就改成了用C语言中的字符串处理。对于字符串处理要注意的几个点:
1) char s[20]
2) gets(s)
3) 子函数中调用char *s
4) strcmp函数将两个字符串进行完全匹配,如果匹配成功返回0,不成功会返回非0值
对于结构体要注意的点:
1) 如果一个类型的结构体只定义一个,像这道题中的代码一样合并起来写是没有问题的。
2) 如果是多个的话,只能拆开来,后面再定义struct trie tree1,tree2;
模板上还有一些细节可以学习:
用宏定义或者typedef偷懒
观察这棵字典树,所有的节点上都是没有字母的,但是会记录下以这个节点前所有箭头上的字母组成的单词作为前缀的单词数。
每一个从编号为root的节点旁边标有c的箭头就是代表tree[root].next[c]。
字典树分为两部分,一部分是建树,还有一部分是查找。
建树部分
从根节点开始,一开始根节点是0,因为要开始建一棵崭新的树,所以有memset(&tree[0],0,sizeof(trie))
取出单词的第一个字母,从根节点开始划这个字母对应的箭头,如果已经有指向了,把指向的节点更新为根节点。
如果没有指向,那么就指向一个新的节点,并把它作为根节点。同理,因为之后要开始建一棵新的字典子树,要初始化一下。
现在可以累加前缀单词的数量。
总之每处理一个字母,就要新划一个箭头或者沿着原有的箭头指向一个新的节点,并且更新到这个节点为止的前缀单词数量。
查找部分比较好理解,就不再赘述。
数据结构还是应该从比较形象的角度入手理解抽象的问题。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define cg(x) x - 'a';
using namespace std;
const int MAXN = 1e6;
struct trie{
int next[26];
int val;
}tree[MAXN];
int cnt;
void insert(char *s)
{
int root = 0;
int l = strlen(s);
int c;
for (int i = 0; i < l; i++)
{
c = cg(s[i]);
if (!tree[root].next[c])
{
memset(&tree[cnt], 0, sizeof(trie));
tree[root].next[c] = cnt++;
}
root = tree[root].next[c];
tree[root].val++;
}
}
int find(char *s)
{
int root = 0;
int l = strlen(s);
int c;
for (int i = 0; i < l; i++)
{
c = cg(s[i]);
if (!tree[root].next[c]) return 0;
else
root = tree[root].next[c];
}
return tree[root].val;
}
int main()
{
char s[20];
memset(&tree[0], 0, sizeof(trie));
cnt = 1;
while (gets(s))
{
if (strcmp(s,"") != 0) insert(s);
else break;
}
while (gets(s))
{
printf("%d\n", find(s));
}
return 0;
}