BZOJ4327 玄武密码 (AC自动机)

题目大意

给出一个母串和一些特征串,询问字串能在母串中匹配的最长的前缀的长度。


题解

将特征串一一插入AC自动机并构建fail指针,这样母串匹配的时候走过的路径上的点所代表的前缀就都是可匹配的了。

所以把所有的特征串插入AC自动机后构造fail树,然后把母串在AC自动机上跑一下并在可匹配的点上留下标记就可以了,这里要注意标记一定要顺着fail指针传上去一并打上标记,否则可能会遗漏。然后对于每一个特征串,从叶节点向根节点找标记,找到的第一个标记就是可匹配的最长前缀。

另外说一下,bzoj里面别再用getchar了,会WAWAWA。老老实实用scanf吧。


代码

#include 
#include 
#include 
#include 
using namespace std;
#define print(k) cout<<#k<<"="<

int turn(char ch) {
    if(ch=='E') return 0;
    if(ch=='S') return 1;
    if(ch=='W') return 2;
    if(ch=='N') return 3;
}

const int maxn=int(1e7)+111;
int fin[maxn],l[maxn];

struct AC_auto {
    int to[maxn][4],fa[maxn],fail[maxn];
    bool cnt[maxn];
    int tot;
    void init() {
        tot=1;
    }

    inline void Insert(char *s,int n,int id) {
        int now=0;
        for(int i=0;iint c=turn(s[i]);
            if(!to[now][c]) fa[tot]=now, to[now][c]=tot++;
            now=to[now][c];
        }
        fin[id]=now; l[id]=n;
        return;
    }

    std::queue<int> que;
    void Build() {
        fail[0]=-1;
        que.push(0);
        while(que.size()) {
            int u=que.front(); que.pop();
            for(int i=0;i<4;i++) if(to[u][i]) {
                int v=to[u][i], p=fail[u];
                if(u==0) {fail[v]=0; que.push(v); continue;}
                while(~p) {
                    if(to[p][i]) {fail[v]=to[p][i]; break;}
                    p=fail[p];
                }
                if(p==-1) fail[v]=0;
                que.push(v);
            }
        }
        return;
    }

    inline void Get(register int u) {
        while(~u && !cnt[u]) {
            cnt[u]=true;
            u=fail[u];
        }
        return;
    }

    void Match(char *s,int n) {
        register int now=0;
        for(register int i=0;iregister int c=turn(s[i]);
            if(to[now][c]) now=to[now][c];
            else {
                int p=fail[now];
                while(~p && !to[p][c]) p=fail[p];
                if(p==-1) now=0;
                else now=to[p][c];
            }
            if(!cnt[now]) Get(now);
        }
    }

    inline int Calc(int id) {
        register int now=fin[id], len=l[id];
        while(now && !cnt[now]) {
            len--;
            now=fa[now];
        }
        return len;
    }
}atm;

int n,m;
char org[maxn],s[maxn];

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE
    scanf("%d%d%*c",&n,&m);
    scanf("%s",org);

    getchar();
    atm.init();
    for(int id=1;id<=m;id++) {
        scanf("%s",s);
        int len=strlen(s);
        atm.Insert(s,len,id);
    }
    atm.Build();
    atm.Match(org,n);

    if(m==1) {printf("%d",atm.Calc(1)); return 0;}
    for(int i=1;i<=m;i++)
        printf("%d\n",atm.Calc(i));

    return 0;
}

你可能感兴趣的:(AC自动机)