zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    07.31《jQuery》——3.2文件上传、添加删除文件域
    07.31《jQuery》——3.1jQuery实现DOM节点的增删改
    07.30《jQuery》——2.2使用键盘上下左右键控制div框
    07.30《jQuery》——2.1div框的移动
    07.30《jQuery》——2.1隔行换色_简单的选择器练习
    【leetcode 530】刷题问题
    数据库基本概念之事务与并发控制
    我的LaTeX中文文档模板
    vim的使用与配置
    LaTeX简单使用方法
  • 原文地址:https://www.cnblogs.com/qingjiuling/p/11429962.html
Copyright © 2011-2022 走看看