• YBT 2.4 AC自动机


    其实这个专题NOIP几乎不考

    AC自动机,就是能让题自动AC的东西,是不是十分神奇

    对的,就是这么神奇

    AC自动机是解决多模式串与文本串匹配的问题

    是KMP+Trie树的结合,也是一个毒瘤算法

    Keywords Search

    link

    此题是AC自动机的板子,刚才说过AC自动机是解决匹配问题的,这道题便如此

    板子题,记录结尾即可

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    inline int read()
    {
        int f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    int t,n;
    int bo[500005],tot,ch[500005][26];
    char str[1000001];
    void insert(char *str)
    {
        int u=1;
        int len=strlen(str);
        for(int i=0;i<len;i++)
        {
            int c=str[i]-'a';
            if(ch[u][c]==0) ch[u][c]=++tot;
            u=ch[u][c];    
        }
        bo[u]++;
        return;
    }
    int nex[500005],que[500005];
    void bfs()
    {
        que[1]=1,nex[1]=0;
        int head=1,tail=1;
        while(head<=tail)
        {
            int t=que[head]; head++;
            for(int i=0;i<=25;i++)
            {
                if(ch[t][i]==0) ch[t][i]=ch[nex[t]][i];
                else{
                    que[++tail]=ch[t][i];
                    int v=nex[t];
                    nex[ch[t][i]]=ch[v][i];
                }
            }
        }
    }
    int ans;
    void find(char *str)
    {
        int u=1,k;
        int len=strlen(str);
        for(int i=0;i<len;i++)
        {
            int c=str[i]-'a';
            k=ch[u][c],u=ch[u][c];
            
            while(k>1)
            {
                ans+=bo[k];
                bo[k]=0;
                k=nex[k];
            }
            
        }
        return;
    }
    int main()
    {
        t=read();
        while(t--)
        {
            ans=0;
            tot=1;
            memset(ch,0,sizeof(ch));
            memset(bo,0,sizeof(bo));
            for(int i=0;i<=25;i++) ch[0][i]=1;
            n=read();
            for(int i=1;i<=n;i++) 
            {
                scanf("%s",str);
                insert(str);
            }
            
            bfs();
            scanf("%s",str); 
            find(str);
            cout<<ans<<endl;
        }
    }
    View Code

    玄武密码

    link

    此题求模式串在文本串前缀最大匹配长度

    所以让文本串在AC自动机上走一遍,用flag记录经过的点

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    inline int read()
    {
        int f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    int n,m,le[100001];
    char cx(char u)
    {
        if(u=='E') return '0';
        if(u=='S') return '1';
        if(u=='W') return '2';
        if(u=='N') return '3';
    }
    int fa[10000001],ch[1000001][4],tot;
    bool flag[1000001];
    char str[1110];
    int nex[1000010];
    int que[1000010];
    char s[10000010];
    int son[100001];
    void insert(char *str,int an)
    {
        int u=1;
        int len=strlen(str);
        le[an]=len;
        for(int i=0;i<len;i++)
        {
            int c=str[i]-'0';
            if(ch[u][c]==0) ch[u][c]=++tot,fa[tot]=u;
            u=ch[u][c];
        }
        son[an]=u;
        return;
    }
    
    void bfs()
    {
        nex[1]=0,que[1]=1;
        int head=1,tail=1;
        while(head<=tail)
        {
            int x=que[head];head++;
            for(int i=0;i<4;i++)
            {
                if(ch[x][i]==0) ch[x][i]=ch[nex[x]][i];
                else{
                    que[++tail]=ch[x][i];
                    nex[ch[x][i]]=ch[nex[x]][i];
                }
            }
        }
        return;
    }
    int ls;
    void find(char *s)
    {
        int u=1,k;
        for(int i=0;i<ls;i++)
        {
            int c=s[i]-'0';
            k=ch[u][c];
            while(k>1) 
            {
                if(flag[k]) break;
                
                flag[k]=1;
                k=nex[k];
            }
            u=ch[u][c];
        }
        return;
    }
    int work(int an)
    {
        int u=son[an];
        for(int i=le[an];i>=1;i--)
        {
            if(flag[u]) return i;
            u=fa[u];
        }
        return 0;
    }
    int main()
    {
        memset(ch,0,sizeof(ch));
        tot=1;
        n=read(),m=read();
        scanf("%s",s); 
        for(int i=0;i<4;i++) ch[0][i]=1;
        ls=strlen(s);
        for(int i=0;i<ls;i++) s[i]=cx(s[i]);
        for(int i=0;i<4;i++) ch[0][i]=1;
        for(int i=1;i<=m;i++)
        {       
            scanf("%s",str);
            int l=strlen(str);
            for(int j=0;j<l;j++) str[j]=cx(str[j]);
            insert(str,i);
        }
        bfs();
        find(s);
        for(int i=1;i<=m;i++) printf("%d
    ",work(i));//cout<<work(i)<<endl; 
    }
    View Code

    Censoring

    link

    也是让文本串在AC自动机上跑,然后记录一下位置

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<stack>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    inline int read()
    {
        int f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    char str[1000011];
    int n;
    int ch[2600001][26],tot;
    char s[1000011];
    int que[2600001],nex[2600001];
    int bo[2600001];
    void insert(char *s)
    {
        int len=strlen(s);
        int u=1;
        for(int i=0;i<len;i++)
        {
            int c=s[i]-'a';
            if(ch[u][c]==0) ch[u][c]=++tot;
            u=ch[u][c];
        }
        bo[u]=len;
        return;
    }
    void bfs()
    {
        int head=1,tail=1;
        que[1]=1,nex[1]=0;
        while(head<=tail)
        {
            int x=que[head];head++;
            for(int i=0;i<26;i++)
            {
                if(ch[x][i]==0) ch[x][i]=ch[nex[x]][i];
                else{
                    que[++tail]=ch[x][i];
                    nex[ch[x][i]]=ch[nex[x]][i];
                }
            }
        }
        return;
    }
    int stk1[26000001],stk2[26000001];
    void find(char *str)
    {
        int u=1;
        int tot=0;
        int len=strlen(str);
        for(int i=0;i<len;i++)
        {
            int c=str[i]-'a';
            u=ch[u][c];
            stk1[++tot]=u;
            stk2[tot]=i;
            while(bo[u]) 
            {
                tot-=bo[u];
                u= stk1[tot];
            }
        }
        for(int i=1;i<=tot;i++) cout<<str[stk2[i]];
        cout<<endl;
    }
    int main()
    {
        tot=1;
        for(int i=0;i<26;i++) ch[0][i]=1;
        scanf("%s",str);
        n=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s);
            insert(s);
        }
        bfs();
        find(str);
    }
    View Code

    单词(待补此坑)

    最短母串(待补此坑)

    病毒

    link

    此题是不让文本串在AC自动机上跑到每个模式串的末尾

    所以想这个问题,如果说你有无穷长度的文本串

    在AC自动机上跑着,但是不让他经过末尾

    所以说这个图是有环的,AC自动机不是因为节省时间直接相连nex吧,所以好像这个叫做fail图

    所以看一下在AC自动机中不经过每个字符串尾端是否有环即可

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    inline int read()
    {
        int f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    char s[900001];
    int tot,st[9000001];
    int ch[9000001][3];
    int n;
    int quee[3000001],nex[3000001];
    void insert(char *s)
    {
        int len=strlen(s),u=1;
        for(int i=0;i<len;i++) 
        {
            int c=s[i]-'0';
            if(!ch[u][c]) ch[u][c]=++tot;
            u=ch[u][c];
        }
        st[u]=1;
        return;
    }
    void bfs()
    {
        int head=1,tail=1;
        quee[1]=1,nex[1]=0;
        while(head<=tail)
        {
            int x=quee[head];head++;
            for(int i=0;i<=1;i++)
            {
                if(ch[x][i]==0) ch[x][i]=ch[nex[x]][i];
                else{
                    quee[++tail]=ch[x][i];
                    nex[ch[x][i]]=ch[nex[x]][i];
                    if(st[nex[ch[x][i]]]) st[ch[x][i]]++;  
                }
            }
        }
        return;
    }
    int vis1[300011],vis2[300011];
    bool flag;
    void dfs(int f)
    {
        if(flag) return;
        vis2[f]=1;
        for(int i=0;i<=1;i++)
        {
            int s=ch[f][i];
            if(flag) return;
            if(st[s]) continue;
            if(vis2[s]) 
            {
                flag=1;
                cout<<"TAK";
                return;
            }
            if(flag) return;
            if(vis1[s]) continue;
            vis1[s]=1;
            dfs(s);
        }
        vis2[f]=0;
    }
    int main()
    {
        tot=1;
        n=read();
        for(int i=0;i<=1;i++) ch[0][i]=1;
        for(int i=1;i<=n;i++) scanf("%s",s),insert(s);
        bfs();
        flag=false;
        dfs(0);
        if(flag==0) cout<<"NIE";
        return 0;
    }
    /*
    2
    1
    0
    */
    View Code

    文本生成器

    link

    第一次在AC自动机上跑dp

    想一个问题,如果这个有m长度的文本串符合条件的话,他至少是一个模式的母串吧

    你怎么去统计呢,有可能是一个,两个,三个,四个~~~

    在数学上有一种想法是退而求进,所以可以把这道题转换成有多少个无法模式串匹配的情况

    这时候求的就是0了,然后去拿总可能-这种可能便是答案

    为什么说这一道题是dp呢,因为满足dp的思想了

    dp[i]为长度为i有多少个

    但是无法往前推

    所以再加一维,加什么呢

    很容易想到是访问在哪个节点吧

    所以dp模型就已经定下来了

    dp[i][j]表示现在长度为i,节点是j共有多少个串

    AC自动机有什么用呢,其实就是建图有用

    将字符串转换成一张图

    不让他走末尾节点

    所以现在就有一个要注意的坑点了

    ***当你找到一点x,若此点的nex是一个字符串的结尾,所以这个x其实就是一个病毒了

         其实就是每人对nex理解了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define mod 10007
    using namespace std;
    inline long long read()
    {
        long long f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    long long n,m,tot,u,ch[900001][26],bo[900001];
    char s[3000001];
    void insert(char *s)
    {
        long long len=strlen(s),u=1;
        for(long long i=0;i<len;i++)
        {
            long long c=s[i]-'A';
            if(!ch[u][c]) ch[u][c]=++tot;
            u=ch[u][c];
        }bo[u]=1;
        return;
    }
    long long que[3000001],nex[3000001];
    void bfs()
    {
        long long head=1,tail=1;
        que[1]=1,nex[1]=0;
        while(head<=tail)
        {
            long long xx=que[head];head++;
             for(long long i=0;i<26;i++)
            {
                if(ch[xx][i]==0) ch[xx][i]=ch[nex[xx]][i];
                else{
                    que[++tail]=ch[xx][i];
                    nex[ch[xx][i]]=ch[nex[xx]][i]; 
                    bo[ch[xx][i]]|=bo[ch[nex[xx]][i]];
                }
            }
        }
        return;
    }
    long long dp[151][300001];
    int main()
    {
        for(long long i=0;i<26;i++) ch[0][i]=1;
        tot=1;
        n=read(),m=read();
        for(long long i=1;i<=n;i++)
        {
            scanf("%s",s);
            insert(s);
        }
        bfs();
        dp[0][1]=1;
        for(long long i=1;i<=m;i++) 
            for(long long j=1;j<=tot;j++)
                for(long long z=0;z<26;z++) 
                    if(!bo[ch[j][z]]) dp[i][ch[j][z]]+=dp[i-1][j],dp[i][ch[j][z]]%=mod;
        long long ans=0;
        for(long long i=1;i<=tot;i++) ans+=dp[m][i],ans%=mod;
        long long sum=1;
        for(long long i=1;i<=m;i++) sum*=26,sum%=mod;
        cout<<(sum-ans+mod)%mod;
    }
    View Code
  • 相关阅读:
    demo_38 关注页导航栏实现
    demo_37 评论列表实现_02 封装popup 及 格式化时间
    demo_37 评论列表实现_01
    SaaS
    rsyncd脚本
    rsyncd
    MySQL高可用--MHA安装
    正向代理
    zabbix 一键部署
    kvm安装
  • 原文地址:https://www.cnblogs.com/si-rui-yang/p/9709479.html
Copyright © 2020-2023  润新知