AC自动机 模板与简单讲解 模板题:贴瓷砖

43 篇文章 0 订阅
39 篇文章 6 订阅

引入

AC自动机是什么呢
首先要知道KMP,KMP是单字符串匹配
接下来要知道字典树,是用来存一堆字符串
AC自动机就是它们合起来:多字符串匹配

用一个网上很多人举的例子
在一篇文章中找出某些字符串出现的次数就是AC自动机的作用

建trie

fo(i,1,m)
    {
        scanf("%s",t+1);
        scanf("\n");
        int k=1;
        fo(j,1,strlen(t+1))
        {
            int l=t[j]-96;
            if (c[k][l]==0) c[k][l]=++tot;
            deep[c[k][l]]=deep[k]+1;k=c[k][l];  
        }
        c[k][0]=1;//在每个串的结尾标记一下
    }

fail

KMP有个next数组
到了AC自动机,有个一样作用的fail数组
建fail的思想自己YY或找国家队论文,网上很多资料不靠谱
建fail程序

void buildfail()
{
    int j=1,h=0;d[j]=1;
    for(;h<j;)
    {
        int k=d[++h],p=0;
        fo(i,1,26)
        if(c[k][i]!=0)
        {
            int p=c[k][i];
            if(p)
            {
                int z=fail[k];
                while(z!=0&&c[z][i]==0) z=fail[z];
                fail[p]=c[z][i];
                d[++j]=p;
            }
            fail[p]=(fail[p]==0)?1:fail[p];
            up[p]=p;
        }
    }
}

用队列,c数组是trie,第二维就是代表26个字母

查询

查询也就差不多了,对于每一次都从fail链上走过即可

void query()
{
    int k=1;
    fo(i,1,n)
    {
        int j=s[i]-96;
        while(k!=1 && c[k][j]==0) k=fail[k];
        k=c[k][j];k=(k==0)?1:k;
        while (p!=1)
        {
            if(c[p][0]==1) ans++;
            p=fail[p];
            如果不能重复的话就把c[p][0]标记为负数就行了
        }
    }
}

套上题目,灵活运用,加上各种优化。

模板题:贴瓷砖

Description

A镇的主街是由N个小写字母构成,镇长准备在上面贴瓷砖,瓷砖一共有M种,第i种上面有Li个小写字母,瓷砖不能旋转也不能被分割开来,瓷砖只能贴在跟它身上的字母完全一样的地方,允许瓷砖重叠,并且同一种瓷砖的数量是无穷的。
问街道有多少字母(地方)不能被瓷砖覆盖。

Input

第一行输入街道长度N(1<=N<=300,000)。
第二行输入N个英文小写字母描述街道的情况。
第三行输入M(1<=M<=5000),表示瓷砖的种类。
接下来M行,每行描述一种瓷砖,长度为Li(1<=Li<=5000),全部由小写字母构成。

Output

输出有多少个地方不能被瓷砖覆盖。

Sample Input

输入1:
6
abcbab
2
cb
cbab
输入2:
4
abab
2
bac
baba
输入3:
6
abcabc
2
abca
cab

Sample Output

输出1:
2
输出2:
4
输出3:
1

Data Constraint

N(1<=N<=300,000)

这题就是AC自动机的裸题

加个小优化:用up[x]表示x沿fail链上的第一个有值的点,这样就不用循环了
求答案就线段覆盖咯~~

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 301000
#define M 5010
using namespace std;
char s[N],t[M];
int n,m,fail[M*1000],deep[M*1000],c[M*800][28],ans[N][2],bz[N],up[M*1000],tot=1,d[M*1000];
void buildfail()
{
    int j=1,h=0;d[j]=1;
    for(;h<j;)
    {
        int k=d[++h],p=0;
        fo(i,1,26)
        if(c[k][i]!=0)
        {
            int p=c[k][i];
            if(p)
            {
                int z=fail[k];
                while(z!=0&&c[z][i]==0) z=fail[z];
                fail[p]=c[z][i];
                d[++j]=p;
            }
            fail[p]=(fail[p]==0)?1:fail[p];
            up[p]=p;
            if(c[up[p]][0]==0&&up[p]!=1) up[p]=up[fail[p]];//求up
        }
    }
}
void query()
{
    int k=1;
    fo(i,1,n)
    {
        int j=s[i]-96;
        while(k!=1 && c[k][j]==0) k=fail[k];
        k=c[k][j];k=(k==0)?1:k;
        ans[++ans[0][0]][0]=i;ans[ans[0][0]][1]=deep[up[k]];
        //因为题目的特殊,求答案也可以特殊一点
    }
}
int main()
{
    freopen("ACauto.in","r",stdin);
    scanf("%d\n",&n);
    scanf("%s",s+1);
    scanf("%d\n",&m);
    fo(i,1,m)
    {
        scanf("%s",t+1);
        scanf("\n");
        int k=1;
        fo(j,1,strlen(t+1))
        {
            int l=t[j]-96;
            if (c[k][l]==0) c[k][l]=++tot;
            deep[c[k][l]]=deep[k]+1;k=c[k][l];  
        }
        c[k][0]=1;
    }
    buildfail();
    query();
    fo(i,1,ans[0][0])
    {
        bz[ans[i][0]+1]--;bz[ans[i][0]-ans[i][1]+1]++;
    }
    int an=0,jy=0;
    fo(i,1,n)
    {
        jy+=bz[i];if(jy==0) an++;
    }
    printf("%d",an);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值