AC自动机 模板 【HDU2222】 Keywords Search

AC自动机(Aho Corasick Automaton)
如果匹配字符串,一个模式串,一个匹配串,KMP就搞定了。
但是如果现在有多个模式串怎么办呢?每个模式串都处理一遍,再与匹配串匹配就太慢了,所以有了AC自动机,来解决这类问题。

AC自动机可以看成是Trie树和KMP的结合体,具体的实现方式就是在每个节点处增添一个fail指针,作用与KMP中的失配时用的跳转数组差不多(虽然在匹配的大多数时候都用不到它)。

但是fail指针最大的作用是AC自动机的建树,让一个状态的每个儿子都能进行状态转移(其实这里我已经偷换概念了QAQ,这个已经是Trie图,而不是AC自动机了)。

还有一个问题就是一个模式串可能包含另一个模式串,这时候可以通过跳fail指针来预处理该到该节点一共有多少单词。
(说的并不详细,但模板可用,如果想了解更多更深,还是去找其他神犇的博客)

【HDU2222 Keywords Serch】
题目大意:
给出n个串,然后给一篇文章,问这n个串有多少个在文章里面出现过
n个串中可能有完全相同的两个串,当做多个串
同一个串在文章中出现多次,不多次计算

题目分析:
AC自动机把所有串装里,然后把匹配串仍在里面跑一遍,途中搜集单词,最终输出答案。

注意事项:
1、注意走过的路要打标记,打了标记的地方有单词也不能加,会加重;
2、在写建树函数的时候l和r不能static(这个应该不会有别人和我犯一样的傻错误)。
代码如下:

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cstdlib>
#define N 1200000
using namespace std;
int n,T;
char c[60],s[N];
struct trie{
    trie *son[26],*fail;
    int dc;bool lz;
    trie()
    {
        memset(son,0,sizeof(son));
        dc=0;fail=NULL;lz=false;
    }
    void inser(char *s)
    {
        trie *c=this;
        for(int i=0;s[i];i++)
        {
            if(!c->son[s[i]-'a']) c->son[s[i]-'a']=new trie();
            c=c->son[s[i]-'a'];
        }
        c->dc++;
    }
}*V;
void AC_automation()
{
    static trie* dl[N];
    int l=1,r=0;
    for(int i=0;i<26;i++)
    {
        if(!V->son[i]) V->son[i]=V;
        else {V->son[i]->fail=V;dl[++r]=V->son[i];}
    }
    trie *c,*t;
    while(l<=r)
    {
        c=dl[l++];
        for(int i=0;i<26;i++)
        {
            if(!c->son[i]) c->son[i]=c->fail->son[i];
            else {c->son[i]->fail=c->fail->son[i];dl[++r]=c->son[i];}
        }
        t=c->fail;
        while(t!=V && !t->dc) t=t->fail;
        c->fail=t;
    }
}
int query(char *s)
{
    trie *c=V,*t;int ans=0;
    for(int i=0;s[i];i++)
    {
        c=c->son[s[i]-'a'];
        t=c;
        while(t!=V && !t->lz) {t->lz=true;ans+=t->dc;t=t->fail;}
    }
    return ans;
}
int main()
{
    scanf("%d",&T);
while(T--)
{
    scanf("%d",&n);
    V=new trie();
    for(int i=1;i<=n;i++)
    {
        scanf("%s",c);
        V->inser(c);
    }
    AC_automation();
    scanf("%s",s);
    int ans=query(s);
    printf("%d\n",ans);
}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值