题目描述
输入
输出
示例输入
5 3 Inkfish Henry Carp Max Jericho Carp Max Carp 0
示例输出
3
来源
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
struct node
{
int flag;
struct node *next[26];
};
char s1[20001][20],s2[20001][20];
struct node *newnode()
{
struct node *p;
int i;
p=(struct node*)malloc(sizeof(struct node));
p->flag=0;
for(i=0; i<=25; i++)
{
p->next[i]=NULL;
}
return p;
};
void insert(struct node*root,char *s)
{
int t,len=strlen(s),i;
struct node*p=root;
for(i=0; i<=len-1; i++)
{
if('a'<=s[i]&&s[i]<='z')
t=s[i]-'a';
else
t=s[i]-'A';
if(p->next[t]==NULL)//每一个节点中某个字母的位置为空,在该位置新开出一片空间,则该位置非空表示有元素
{
p->next[t]=newnode();
}
p=p->next[t];// 原本给位置已经有这个元素不需开空间,指针指向该位置则可
}
p->flag=1;//字符串处理结束时将最后一个字母位置节点标记为1
}
int search(struct node*root,char *s)
{
int t,len=strlen(s),i;
struct node*p=root;
for(i=0; i<=len-1; i++)
{
if('a'<=s[i]&&s[i]<='z')
t=s[i]-'a';
else
t=s[i]-'A';
if(p->next[t]==NULL)
return 0;
p=p->next[t];
}
if(p->flag)//查找过程如果成功遍历到最后则最后一个字母节点的flag为1
return 1;
else return 0;
}
int main()
{
int n,m,i,count;
struct node *root;
while(~scanf("%d %d",&n,&m))
{
if(n==0)
break;
count=0;
root=newnode();
for(i=0; i<=n-1; i++)
{
scanf("%s",s1[i]);
}
for(i=0; i<=m-1; i++)
{
scanf("%s",s2[i]);
}
for(i=0;i<=m-1;i++)
{insert(root,s2[i]);}
for(i=0;i<=n-1;i++)
{if(search(root,s1[i])==0)//输出剩下人的个数
count++;}
printf("%d\n",count);
}
return 0;
}
我们选的字典树的节点的结构表达为:
typedef struct node{
struct node *br[26];
int num;
} 其中的 br[26]是对应于每个节点下面的可能有的26个字母,因为对于一个单词而言,每个字母后面的字母只有26种可能嘛(如果只是允许大写或者是小写的话),而且在后面大家可能会发现,对于某个不为空的节点,它的子节点所对应的字母是按照字母表次序来的,然后num就是指该节点的孩子节点的数目,也就是以从根节点到该节点的路径上所有字母组成的字符串为前缀的字符串的数目。
然后需要定义一个全局的头指针,因为每次对字符串开始进行插入操作的时候,都需要从头结点开始,这样才可以利用公共前缀,减少存储空间,还有加快查找速度;
接着就是字典树的构建问题,其实就是一个for循环,对你所要插入字典树的字符串的每一位字符进行插入操作,同时我们定义两个Node 指针,一个t,用于中间的新建节点,因为当你查找到某个节点下面没有你这个字符所对应的节点的时候,你就需要自己新建一个节点用以保存你这个字符,另外一个指针s,就是用于对头指针开始操作,作用前面已经说了,接着就开始建树,对字符串str,对每一次循环,如果当前指针s下面的对应于当前字符str[i]在字母表的位置的孩子节点为空,就说明此时该字符串str于其他的字符的公共前缀最多延伸到s处,接着就需要新建节点了,同时对新建节点以及新建节点的孩子进行初始化(尤其是num的初始化),如果当前指针s下面的对应于当前字符str[i]在字母表的位置的孩子结点不为空,那么此时就不需要进行新建节点,指针向下移动即可.这样对输入的每个字符串进行插入操作,就可以构建出一棵字典树了:
void Tree_Insert(char str[]){
Node *t,*s = head;
int i,j;
int len = strlen(str) - 1;
for(i = 0;i <= len;i ++){
int id = str[i] - 'a';
if(s->br[id] == NULL){
t = new Node;
for(j = 0;j <= 25;j ++){
t->br[j] = NULL;
}
t->num = 0;
s->br[id] = t;
}
s = s->br[id];
s->num ++;
}
}
然后就是查找,需要一个指针用以从头结点开始操作,也是对每个要进行查找的字符串str进行遍历,树则从头指针开始,如果当前指针s的对应于当前字符str[i]在字母表的位置的子节点不为空,那么久说明此时可以继续查找str中的下一个字符,如果为空的话,那么就说明此时这棵树中没有以str为公共前缀的字符串,那么久可以返回为0,而对于第一种不为空的情况,每次查找都用count保存当前指针的孩子节点(不为空)的num值,最终得到的就是以str为前缀的字符串的数目;
int Tree_Find(char str[]){
Node *s = head;
int count;
int len = strlen(str) - 1;
for(int i = 0;i <= len;i ++){
int Id = str[i] - 'a';
if(s->br[Id] == NULL){
count = 0;
return count ;
}
else{
s = s->br[Id];
count = s->num;
}
}
return count;
}