HDU 2222【AC自动机】

题意:

第一行输入测试数据的组数,然后输入一个整数n,接下来的n行每行输入一个单词。
最后输入一个字符串,问在这个字符串中有多少个单词出现过。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Node
{
    int cnt;//是否为该单词的最后一个结点
    Node *fail;//失败指针
    Node *next[26];//Trie中每个结点的各个节点
}*queue[500005];//队列,方便用BFS构造失败指针
char s[1000005];//主字符串
char keyword[55];//需要查找的单词
Node *root;//头结点
void Init(Node *root)//每个结点的初始化
{
    root->cnt=0;
    root->fail=NULL;
    for(int i=0;i<26;i++)
        root->next[i]=NULL;
}
void Build_trie(char *keyword)//构建Trie树
{
    Node *p,*q;
    int i,v;
    int len=strlen(keyword);
    for(i=0,p=root;i<len;i++)
    {
        v=keyword[i]-'a';
        if(p->next[v]==NULL)
        {
            q=(struct Node *)malloc(sizeof(Node));
            Init(q);
            p->next[v]=q;//结点链接
        }
        p=p->next[v];//指针移动到下一个结点
    }
    p->cnt++;//单词最后一个结点cnt++,代表一个单词
}
void Build_AC_automation(Node *root)
{
    int head=0,tail=0;//队列头、尾指针
    queue[head++]=root;//先将root入队
    while(head!=tail)
    {
        Node *p=NULL;
        Node *temp=queue[tail++];//弹出队头结点
        for(int i=0;i<26;i++)
        {
            if(temp->next[i]!=NULL)//找到实际存在的字符结点
            { //temp->next[i] 为该结点,temp为其父结点
                if(temp==root)//若是第一层中的字符结点,则把该结点的失败指针指向root
                    temp->next[i]->fail=root;
                else
                {
                    //依次回溯该节点的父节点的失败指针直到某节点的next[i]与该节点相同,
                    //则把该节点的失败指针指向该next[i]节点;
                    //若回溯到 root 都没有找到,则该节点的失败指针指向 root
                    p=temp->fail;//将该结点的父结点的失败指针给p
                    while(p!=NULL)
                    {
                        if(p->next[i]!=NULL)
                        {
                            temp->next[i]->fail=p->next[i];
                            break;
                        }
                        p=p->fail;
                    }
                    //让该结点的失败指针也指向root
                    if(p==NULL)
                        temp->next[i]->fail=root;
                }
                queue[head++]=temp->next[i];//每处理一个结点,都让该结点的所有孩子依次入队
            }
        }
    }
}
int query(Node *root)
{ //i为主串指针,p为模式串指针
    int i,v,count=0;
    Node *p=root;
    int len=strlen(s);
    for(i=0;i<len;i++)
    {
        v=s[i]-'a';
        //由失败指针回溯查找,判断s[i]是否存在于Trie树中
        while(p->next[v]==NULL && p!=root)
            p=p->fail;
        p=p->next[v];//找到后p指针指向该结点
        if(p==NULL)//若指针返回为空,则没有找到与之匹配的字符
            p=root;
        Node *temp=p;//匹配该结点后,沿其失败指针回溯,判断其它结点是否匹配
        while(temp!=root)//匹配结束控制
        {
            if(temp->cnt>=0)//判断该结点是否被访问
            {
                count+=temp->cnt;//由于cnt初始化为 0,所以只有cnt>0时才统计了单词的个数
                temp->cnt=-1;//标记已访问过
            }
            else//结点已访问,退出循环
                break;
            temp=temp->fail;//回溯 失败指针 继续寻找下一个满足条件的结点
        }
    }
    return count;
}
int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        root=(struct Node *)malloc(sizeof(Node));
        Init(root);
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("\n%s",keyword);
            Build_trie(keyword);
        }
        Build_AC_automation(root);
        scanf("\n%s",s);
        printf("%d\n",query(root));
    }
    return 0;
}

学习链接:

http://blog.csdn.net/liu940204/article/details/51347064

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值