jzoj1566 单词查找 AC自动机

题目

给一个主串S以及M个字符串。问这M个字符串在主串中出现多少次。

题解

AC自动机模板
哈哈,表达一下激动之情,我终于写完了!!!


AC自动机=Trie树+fail指针(形同KMP的next数组)
是一种多模匹配算法,就是同时与多个字符串比对
主要包括三个步骤:建字典树,处理fail指针,匹配

建字典树是没什么好注意的

fail指针的作用是在匹配失败时可以继续匹配,不丢失匹配失败前未计入答案的匹配记录
而处理fail指针概括起来就是:沿着x父亲的fail指针走,若指向的节点儿子中有与x相同的点,那么x的fail指针指向与它相同的这个点,否则继续沿着指向的节点的fail指针走,重复步骤,知道fail指针指向树根,那么x的fail指针指向树根
具体做法用广搜进行,先把根节点加入队列,然后把它的儿子们也加入队列,处理出儿子们的fail指针;继续调出队头节点,把它的儿子们加入队列,处理出儿子们的fail指针

匹配具体做法是:设上一个匹配的是节点x,这一次串匹配到第i位,那么若节点x的儿子中有字符串中i所对应的字符,那么这一步匹配到这个符合条件的儿子,否则一直沿着x的fail指针走,直到有与i相同的字符或到达根节点。每次匹配成功一个节点都判断当前点是否是m个字符串的结尾之一,加上答案,还要把当前点的fail指针指向的点加入答案

代码

#include <cstdio>
#include <cstring>

using namespace std;

int t[60004][27],b[30004];
int fail[30004];
int cnt=1,s,m,ans;
char c[2000006];
int q[30004];

void insert(){
    int j=1,len=strlen(c);
    for (int i=0;i<len;i++){
        int ch=c[i]-'a';
        if (t[j][ch]) j=t[j][ch]; 
        else{
            t[j][ch]=++cnt;
            j=cnt;
        }
    }
    b[j]++;
}

void AC_mach(){
    q[1]=1;
    fail[1]=0;
    int l=0,r=1,now;
    while (l<=r){
        l++;
        now=q[l];
        for (int i=0;i<26;i++)
            if (t[now][i]){
                r++;
                q[r]=t[now][i];
                fail[t[now][i]]=1;
                int j=fail[now];
                while (j){
                    if (!t[j][i]) j=fail[j]; else
                    {
                        fail[t[now][i]]=t[j][i];
                        break;
                    }
                }
            }
    }
}

void AC_work(){
    int k=1;
    for (int l=0;l<s;l++){
        int ch=c[l]-'a';
        while (!t[k][ch]) 
           k=fail[k];
        k=t[k][ch];
        for (int j=k;j;j=fail[j])
            ans+=b[j];
    }

}

int main(){
    scanf("%d",&s);
    scanf("%d",&m);
    for (int i=1;i<=m;i++){
        scanf("%s",c);
        insert();
    }
    AC_mach();
    scanf("%s",c);
    AC_work();  
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yjy_aii/article/details/81123294
今日推荐