题目大意
给定N个单词,求满足下列条件的前缀集合S:
- 集合中任意前缀对应的单词数量小于等于5
- 对于集合中任意前缀
p
,p
的扩展前缀不属于该集合
对于第二个条件,举个例子来说:
假设ab
对应了5个单词,abc
对应了3个单词,abd
对应了2个单词。
因为ab
对应的单词数量少于等于5,所以ab
属于集合S。虽然abc
和abd
对应的单词数量均小于等于5,但由于其为ab
的扩展,所以不属于S。
解题思路
由于本题需要询问多个单词的公共前缀,很显然的是一道Trie树的题目,关于Trie树的讲解,可以点击这里学习。
首先我们将所有的单词建立成一颗Trie树,举个例子:
对于树上任意一个节点,表示从根到节点路径构成的前缀,其数字表示该前缀对应的单词数量。
题目中描述的两个条件很容易在该Trie树中判定:
- 当节点数字小于等于5时,则表示该前缀是一个合法解
- 当节点为合法解时,以它为根的子树上其他节点都不再计入合法解
在上图中,蓝色节点表示最后属于集合S的前缀。
由此我们可以得到一个简单的算法:
- 对构建好的Trie树进行遍历
- 若当前节点计数小于等于5,返回1
- 若当前结点计数大于5,递归对所有子树进行遍历,并返回统计和
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,ans;
char str[1000000];
struct node
{
struct node *next[26];
int num;
}Node;
void add(node *root,char *s)
{
int len = strlen(s);
node *p = root;
for(int i = 0; i < len; i++)
{
if(p->next[s[i]-'a'] == NULL)
{
p->next[s[i]-'a'] = (node *)malloc(sizeof(node));
for(int j = 0; j < 26; j++)
p->next[s[i]-'a']->next[j] = NULL;
p->next[s[i]-'a']->num = 0;
}
p = p->next[s[i]-'a'];
p->num++;
}
}
void dfs(node *root)
{
if(root == NULL) return;
if(root->num <= 5 && root->num != 0)
{
ans++;
return;
}
for(int i = 0; i < 26; i++)
{
dfs(root->next[i]);
}
return;
}
void del(node *root)
{
if(root == NULL) return;
for(int i = 0; i < 26; i++)
{
del(root->next[i]);
free(root->next[i]);
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
node *root = (node *)malloc(sizeof(node));
for(int i = 0; i < 26; i++)
root->next[i] = NULL;
root->num = 0;
for(int i = 1; i <= n; i++)
{
getchar();
scanf("%s",str);
add(root,str);
memset(str,0,sizeof(str));
}
ans = 0;
dfs(root);
printf("%d\n",ans);
del(root);
free(root);
}
return 0;
}