Message Flood(字典树)

题目描述

Well, how do you feel about mobile phone? Your answer would probably be something like that "It's so convenient and benefits people a lot". However, If you ask Merlin this question on the New Year's Eve, he will definitely answer "What a trouble! I have to keep my fingers moving on the phone the whole night, because I have so many greeting message to send!" Yes, Merlin has such a long name list of his friends, and he would like to send a greeting message to each of them. What's worse, Merlin has another long name list of senders that have sent message to him, and he doesn't want to send another message to bother them Merlin is so polite that he always replies each message he receives immediately). So, before he begins to send message, he needs to figure to how many friends are left to be sent. Please write a program to help him. Here is something that you should note. First, Merlin's friend list is not ordered, and each name is alphabetic strings and case insensitive. These names are guaranteed to be not duplicated. Second, some senders may send more than one message to Merlin, therefore the sender list may be duplicated. Third, Merlin is known by so many people, that's why some message senders are even not included in his friend list.

输入

There are multiple test cases. In each case, at the first line there are two numbers n and m (1<=n,m<=20000), which is the number of friends and the number of messages he has received. And then there are n lines of alphabetic strings(the length of each will be less than 10), indicating the names of Merlin's friends, one per line. After that there are m lines of alphabetic strings, which are the names of message senders. The input is terminated by n=0.

输出

For each case, print one integer in one line which indicates the number of left friends he must send.

示例输入

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值