• 【字符串】AC自动机


    AC自动机

    学习资料: OI Wiki

    模板

    解释 fail 指针:

    将构建后的字典树的所有状态集合记作 (Q)

    状态 (u) 的 fail 指针指向另一个状态 (v) ,其中 (vin Q) ,且 (v)(u) 的最长后缀(即在若干个后缀状态中取最长的一个作为 fail 指针)。

    AC自动机在做匹配时,同一位上可匹配多个模式串。

    struct AC{
        int tr[maxn][26],cnt;
        int fail[maxn];
        
        
        int ed[maxn],num[maxn];//这两个是根据题意写的
        
        void init(){
            cnt=0;
            mem(tr,0);mem(fail,0);
            
            mem(ed,0);mem(num,0);
        }
        void insert(char s[],int len,int id)
        {
            int p=0;
            for(int i=0;i<len;i++)
            {
                if(!tr[p][s[i]-'a'])tr[p][s[i]-'a']=++cnt;
                p=tr[p][s[i]-'a'];
            }
            
            ed[p]=id;//对于字符串结尾节点的记录,根据题意记录 如这是记录该字符串的下标(属于第几个字符串)
            // 根据题意,有时是单纯的bool标记,有时是记录长度 等等
        }
        
        void getfail()// 当完成插入时,需要 ac.getfaile() 跑一次。
        {
            queue<int>que;
            for(int i=0;i<26;i++)if(tr[0][i])que.push(tr[0][i]);
            while(!que.empty())
            {
                int p=que.front();que.pop();
                for(int i=0;i<26;i++)
                {
                    if(tr[p][i])fail[tr[p][i]]=tr[fail[p]][i],que.push(tr[p][i]);
                    else tr[p][i]=tr[fail[p]][i];
                }
            }
        }
        
        // query 是文本串的匹配,根据题意写。
        // 但无外乎就是这些操作
        void query(char s[],int len,int n)
        {
            int j,p=0;
            for(int i=0;i<len;i++)
            {
                j=p=tr[p][s[i]-'a'];
                // while跑的是 以当前后缀不断遍历字典树里的所有相同后缀 
                while(j)
                {
                    if(ed[j])
                        num[ed[j]]++;//这一句根据题意写
                    j=fail[j];
                }
            }
            //之后的操作也是根据题意
        }
    }ac;
    

    例题

    1,luoguP3796【模板】AC自动机(加强版)

    题意:给 (n) 个模式串,之后给一个文本串。对每组数据,第一行输出模式串最多出现的次数,接下去若干行输出一个出现次数最多的模式串,按驶入顺序排列。

    解:

    对于每个状态终点记录一下该终点对应哪个模式串。之后遍历文本串的时候吗,在fail转移的时候,对每个状态终点记录其对应的出现次数。

    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+50;
    
    char s[maxn];
    char ss[220][110];
    struct AC{
        int tr[maxn][26],cnt;
        int fail[maxn];
        int ed[maxn],num[maxn];
        void init(){
            cnt=0;
            mem(tr,0);mem(fail,0);
            mem(ed,0);mem(num,0);
        }
        void insert(char s[],int len,int id)
        {
            int p=0;
            for(int i=0;i<len;i++)
            {
                if(!tr[p][s[i]-'a'])tr[p][s[i]-'a']=++cnt;
                p=tr[p][s[i]-'a'];
            }
            ed[p]=id;
        }
        void getfail()
        {
            queue<int>que;
            for(int i=0;i<26;i++)if(tr[0][i])que.push(tr[0][i]);
            while(!que.empty())
            {
                int p=que.front();que.pop();
                for(int i=0;i<26;i++)
                {
                    if(tr[p][i])fail[tr[p][i]]=tr[fail[p]][i],que.push(tr[p][i]);
                    else tr[p][i]=tr[fail[p]][i];
                }
            }
        }
        void query(char s[],int len,int n)
        {
            int j,p=0;
            for(int i=0;i<len;i++)
            {
                j=p=tr[p][s[i]-'a'];
                while(j)
                {
                    if(ed[j])num[ed[j]]++;
                    j=fail[j];
                }
            }
            int ma=0;
            for(int i=1;i<=n;i++)ma=max(ma,num[i]);
            printf("%d
    ",ma);
            for(int i=1;i<=n;i++)if(num[i]==ma)puts(ss[i]);
        }
    }ac;
    int main()
    {
        int n;
        while(scanf("%d",&n)&&n)
        {
            ac.init();
            for(int i=1;i<=n;i++)
            {
                scanf("%s",ss[i]);
                ac.insert(ss[i],strlen(ss[i]),i);
            }
            ac.getfail();
            scanf("%s",s);
            ac.query(s,strlen(s),n);
        }
    }
    

    2,牛客 破忒头的匿名信

    题意:给 (n) 个模式串 (a_i) ,对于每个模式串 (a_i) 有一个价值 (p_i) 。之后一个文本串 (T) ,求问最小价值。如果不能通过模式串拼接成文本串,则输出 -1。

    解:

    ac自动机+dp

    (f[i]=min(f[i],f[i-len[p]]+val[i]))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=5e5+5;
     
    ll f[maxn];
    int val[maxn],dep[maxn];
    struct AC{
        int tr[maxn][26],cnt;
        int fail[maxn];
        void insert(char s[],int len,int w)
        {
            int p=0;
            for(int i=0;i<len;i++)
            {
                if(!tr[p][s[i]-'a'])tr[p][s[i]-'a']=++cnt;
                p=tr[p][s[i]-'a'];
            }
            dep[p]=len;
            if(!val[p])val[p]=w;
            val[p]=min(val[p],w);
        }
        void getfail()
        {
            queue<int>que;
            for(int i=0;i<26;i++)if(tr[0][i])que.push(tr[0][i]);
            while(!que.empty())
            {
                int p=que.front();que.pop();
                for(int i=0;i<26;i++)
                {
                    if(tr[p][i])fail[tr[p][i]]=tr[fail[p]][i],que.push(tr[p][i]);
                    else tr[p][i]=tr[fail[p]][i];
                }
            }
        }
        void query(char s[],int len)
        {
            int j,p=0;
            for(int i=1;i<=len;i++)
            {
                j=p=tr[p][s[i]-'a'];
                while(j)
                {
                    if(dep[j])f[i]=min(f[i],f[i-dep[j]]+val[j]);
                    j=fail[j];
                }
            }
        }
    }ac;
    char s[maxn];
    int main()
    {
        int n,w,len;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%s%d",s,&w);
            ac.insert(s,strlen(s),w);
        }
        ac.getfail();
        scanf("%s",s+1);
        len=strlen(s+1);
        for(int i=1;i<=len;i++)f[i]=1e16;
        ac.query(s,len=strlen(s+1));
        printf("%lld
    ",f[len]==1e16?-1:f[len]);
    }
    
  • 相关阅读:
    我国大部分地区今晚将上演红色月全食美景[2007828]
    n阶汉诺塔问题(Hanoi)
    NBear中使用WhereClip表示in查询的问题
    web性能测试分析理论篇
    Net类库一览 转
    使用ASP.Net Forms模式实现WebService身份验证
    2005数据源的相关数据绑定
    单位分级,涵闸和人员绑定到树
    ajax,js文件中中文显示成乱码的问题
    WinFom应用程序数据操作技巧
  • 原文地址:https://www.cnblogs.com/kkkek/p/13796593.html
Copyright © 2020-2023  润新知