P2444 [POI2000]病毒

P2444 [POI2000]病毒

AC自动机(模板题)

题解传送门

一个重要的性质(同样适用于KMP):一个串末尾节点的fail数组指向的子串一定是这个串的后缀

(我真的不想再忘了)

我们的目标是在AC自动机的图中找到一个环,里面没有任何病毒标记

和以往的AC自动机不同,我们在匹配的过程中要避开病毒标记,可以用dfs实现

用ins数组存当前路径,vis数组标记哪些点已被访问过

end.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct data{
    int fail,nxt[2],end;
}a[30002];
int n,cnt;
char q[30002];
bool vis[30002],ins[30002],ok;
inline void Trie_build(){
    scanf("%s",q);
    int u=0,len=strlen(q);
    for(int i=0;i<len;++i){
        int p=q[i]-'0';
        if(!a[u].nxt[p]) a[u].nxt[p]=++cnt;
        u=a[u].nxt[p];
    }++a[u].end;
}
inline void AC_build(){ //普通的AC自动机核心
    queue <int> h;
    for(int i=0;i<2;++i) if(a[0].nxt[i]) h.push(a[0].nxt[i]);
    while(!h.empty()){
        int x=h.front(); h.pop();
        for(int i=0;i<2;++i){
            if(a[x].nxt[i]){
                a[a[x].nxt[i]].fail=a[a[x].fail].nxt[i];
                h.push(a[x].nxt[i]);
                if(a[a[a[x].nxt[i]].fail].end) a[a[x].nxt[i]].end=1; //如果这个串的后缀是病毒那么它也一定是病毒
            }else a[x].nxt[i]=a[a[x].fail].nxt[i]; //优化
        }
    }
}
inline void dfs(int x){ 
    if(ok) return ;
    ins[x]=vis[x]=1;
    for(int i=0;i<2;++i){
        int to=a[x].nxt[i];
        if(ins[to]) {ok=1; break;}
        if(a[to].end||vis[to]) continue;
        dfs(to);
    }ins[x]=0;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i) Trie_build();
    AC_build();
    dfs(0);
    if(ok) printf("TAK");
    else printf("NIE");
    return 0;
}
原文地址:https://www.cnblogs.com/kafuuchino/p/9615408.html