给定N个单词,求满足下列条件的前缀集合S:
p
,p
的扩展前缀不属于该集合对于第二个条件,举个例子来说:
假设ab
对应了5个单词,abc
对应了3个单词,abd
对应了2个单词。
因为ab
对应的单词数量少于等于5,所以ab
属于集合S。虽然abc
和abd
对应的单词数量均小于等于5,但由于其为ab
的扩展,所以不属于S。
由于本题需要询问多个单词的公共前缀,很显然的是一道Trie树的题目,关于Trie树的讲解,可以点击这里学习。
首先我们将所有的单词建立成一颗Trie树,举个例子:
对于树上任意一个节点,表示从根到节点路径构成的前缀,其数字表示该前缀对应的单词数量。
题目中描述的两个条件很容易在该Trie树中判定:
在上图中,蓝色节点表示最后属于集合S的前缀。
由此我们可以得到一个简单的算法:
#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; }