现在的位置: 首页 > 综合 > 正文

hdu 2222 Keywords Search (AC自动机)

2018年02月18日 ⁄ 综合 ⁄ 共 1940字 ⁄ 字号 评论关闭

题目链接:   hdu
2222

题目大意:   给出N个模式串,最后给出主串

                  求模式串在主串的出现过

解题思路:   AC自动机的模版题,关于AC自动机:

                  AC自动机是多模式串匹配的算法,时间复杂度为O(n*m)

                  算法的实现结合了KMP和字典树的思想,其中难点在于理解失败指针

                  先把需要匹配的字符串建成字典树,然后再根据字典树建立失败指针

                  定义1:从距离根节点K/2的结点到a结点的字符串为S1

                  定义2:从根节点到b结点的字符串为S2

                  若S1==S2则a的失败指针指向b,若找不到与S1相等的字符串则a的失败指针指向root

                  如图:

                  

                  

代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 1000010
struct snode{
    int w,fail;
    int next[26];
}Tree[250000];
int Index,list[10000];
char ch[MAX],ch1[100];

void Insert(int Star,int Tlen,int k)  //建立字典树
{
    int i,S=Star,child;
    for(i=1;i<=Tlen;i++)
    {
        child=ch1[i-1]-'a';
        if(Tree[S].next[child]==-1)
        {
            Tree[S].next[child]=Index++;
            Tree[Index-1].w=0;
            if(i==Tlen)
                Tree[Index-1].w=1;
            S=Tree[S].next[child];
        }
        else
        {
            if(i==Tlen)
                Tree[Tree[S].next[child]].w++;
            S=Tree[S].next[child];
        }
    }
}

void Buill_fail(int Star)     //建立失败指针
{
    int i,e,s,now,now_fail;
    s=e=0;
    Tree[0].fail=-1;
    list[s++]=Star;
    while(e!=s)
    {
        now=list[e++];
        for(i=0;i<26;i++)
        {
            if(Tree[now].next[i]!=-1)    // ** -1
            {
                now_fail=Tree[now].fail;
                while(now_fail!=-1)
                {
                    if(Tree[now_fail].next[i]!=-1)  //改变孩子的失败指针  ** -1
                    {
                        Tree[Tree[now].next[i]].fail=Tree[now_fail].next[i];
                        break;
                    }
                    now_fail=Tree[now_fail].fail;
                }
                if(now_fail==-1)      //不存在则把它孩子的失败指针指向根节点  ** -1
                {
                    Tree[Tree[now].next[i]].fail=0;
                }
                list[s++]=Tree[now].next[i];
            }
        }
    }
}

void DFS(int Star)
{
    int i;
    for(i=0;i<26;i++)
    {
        if(Tree[Star].next[i]!=-1)
        {
            DFS(Tree[Star].next[i]);
        }
    }
    return ;
} 

int Ac_auto(int Tlen)
{
    int i,p=0,temp,child,sum=0;
    for(i=1;i<=Tlen;i++)
    {
        child=ch[i-1]-'a';
        while(Tree[p].next[child]==-1&&p!=0)  //寻找失败指针 **00
        {
            p=Tree[p].fail;
        }
        p=(Tree[p].next[child]==-1)?0:Tree[p].next[child];
        temp=p;
        while(temp!=0&&Tree[temp].w!=-1)  //匹配过的则下次不在匹配
        {
            sum+=Tree[temp].w;
            Tree[temp].w=-1;
            temp=Tree[temp].fail;
        }
    }
    return sum;
}

int main()
{
    int i,t,n;
    scanf("%d",&t);
    while(t--)
    {
        Index=1;
        memset(Tree,-1,sizeof(Tree));
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%s",ch1);
            Insert(0,strlen(ch1),i);  //建立字典树
        }
        Buill_fail(0);  //建立失败指针
        DFS(0);         //匹配
        scanf("%s",ch);
        printf("%d\n",Ac_auto(strlen(ch)));
    }
    return 0;
}

抱歉!评论已关闭.