[BZOJ4327]JSOI2012 玄武密码(AC自动机)

题目描述

传送门

题解

将小串离线然后建立AC自动机
大串在自动机上直接匹配,能匹配的点标1
然后对于每一个点,如果它能匹配,那么它fail指向的点也能匹配
传递一下标记
然后对于每一个小串再查询一下前缀最多到哪里都匹配了

代码

#include
#include
#include
#include
#include
using namespace std;
#define N 10000005

int trans[100];
char s[100005][105],S[N];
int n,m,sz,cnt,l,r,ans,q[N],ch[N][4],fail[N];
bool vis[N];

void insert(char *s)
{
    int len=strlen(s),now=0;
    for (int i=0;iint x=trans[s[i]];
        if (!ch[now][x]) ch[now][x]=++sz;
        now=ch[now][x];
    }
}
void make_fail()
{
    l=0,r=0;
    for (int i=0;i<4;++i)
        if (ch[0][i]) q[++r]=ch[0][i];
    while (lint now=q[++l];
        for (int i=0;i<4;++i)
        {
            if (!ch[now][i])
            {
                ch[now][i]=ch[fail[now]][i];
                continue;
            }
            fail[ch[now][i]]=ch[fail[now]][i];
            q[++r]=ch[now][i];
        }
    }
}
void ac()
{
    int now=0;
    for (int i=0;iint x=trans[S[i]];
        now=ch[now][x];
        vis[now]=1;
    }
}
void calc(char *s)
{
    int len=strlen(s),now=0,dep=0;
    for (int i=0;iint x=trans[s[i]];
        ++dep;
        now=ch[now][x];
        if (vis[now]) ans=max(ans,dep);
    }
}
int main()
{
    trans['E']=0,trans['S']=1,trans['W']=2,trans['N']=3;
    scanf("%d%d",&n,&m);
    scanf("%s",S);
    for (int i=1;i<=m;++i)
    {
        scanf("%s",s[i]);
        insert(s[i]);
    }
    make_fail();
    ac();
    for (int i=r;i>=1;--i)
    {
        int now=q[i];
        vis[fail[now]]|=vis[now];
    }
    for (int i=1;i<=m;++i)
    {
        ans=0;
        calc(s[i]);
        printf("%d\n",ans);
    }
}

你可能感兴趣的:(题解,AC自动机,省选)