AC自动机 POI2000病毒

题目链接:https://www.luogu.org/problem/P2444

题意:给你一些字符串,问能不能找到一个无限长的字符串,使得给定的这些字符串不会出现在该无限长字符串中

一般我们写ac自动机都是尽可能的使多匹配,而本题反其道而行,要尽可能的不匹配,那么我们可以遇到fail标记就跳(因为一个字符串的标记是在最后,中途就调走了肯定就不会遇到了)。如果存在一个无限长的字符串,那么我们内部肯定会形成一个环,且这个环中不会有带结束标记的点,且这个环一定要包含根节点。

PS:这题数据量非常小,我自己想过数据量大了可以结合拓扑排序,但入度不是指向fail边的起点,而是终点,这样可以从小的更新大的,但最后拓扑排序fake了,我做不出来,只能用题解方法了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=30007;
const int inf=0x3f3f3f3f;
const int N=1e7;
const ll mod=998244353;
#define meminf(a) memset(a,0x3f,sizeof(a))
#define mem0(a) memset(a,0,sizeof(a))
char a[maxn];
struct node{
    int end;
    int vis[2];
    int fail;
}ac[maxn];
int cnt=0;
bool v[maxn],w[maxn];
//分别表示结点i是否在当前路径当中,以及结点i之前是否被访问过 
void insert(char * s){
    int len=strlen(s);
    int now=0;
    for(int i=0;i<len;i++){
        if(ac[now].vis[s[i]-48]==0) ac[now].vis[s[i]-48]=++cnt;
        now=ac[now].vis[s[i]-48];
    }
    ac[now].end=1;
}
void get_fail(){
    queue<int> que;
    if(ac[0].vis[0]!=0)que.push(ac[0].vis[0]);
    if(ac[0].vis[1]!=0)que.push(ac[0].vis[1]);
    while(!que.empty()){
        int u=que.front();que.pop();
        for(int i=0;i<=1;i++){
            if(ac[u].vis[i]!=0){
                int temp=ac[u].fail;
                que.push(ac[u].vis[i]);
                while(temp>0&&ac[temp].vis[i]==0) temp=ac[temp].fail;
                //这个while循环是优化的关键,如果最小的病毒字符串被标记了,那么所有包含它的字符串也都该被标记,
                //所以我们要找到最长匹配后缀串,我们要一直循环,直到找到fail指针跳向的结点的下一位也存在,只有这样才和我们当前结点跳向的下一位一致。
                ac[ac[u].vis[i]].fail=ac[temp].vis[i];
                if(ac[ac[temp].vis[i]].end) ac[ac[u].vis[i]].end=1;
            }
            else ac[u].vis[i]=ac[ac[u].fail].vis[i];
        }
    }
}
void dfs(int d){
    v[d]=true;
    for(int i=0;i<=1;i++){
        if(v[ac[d].vis[i]]){
            //此时已经找到环了
            printf("TAK
");
            exit(0); 
        }else if(!ac[ac[d].vis[i]].end&&!w[ac[d].vis[i]]){
            w[ac[d].vis[i]]=true;
            dfs(ac[d].vis[i]);
        }
    } 
    v[d]=false;
}
int main(){
    int n;scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%s",a);
        insert(a);
    }
    get_fail();
    dfs(0);
    printf("NIE
");
    return 0;
}
原文地址:https://www.cnblogs.com/qingjiuling/p/11429962.html