AC自动机专题

https://blog.csdn.net/lingzidong/article/details/80714431

AC自动机两大题型:查询字串、建立有向图DP(常数较大串数较少需要矩阵快速幂)

AC自动机学习:18.10.1

HDU2222 Keywords Search

给定n个单词和1个句子,求句子中不同单词的个数

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e6+5;
struct Trie
{
    static const int MAXN=26;
    int nxt[MAX][MAXN],f[MAX],e[MAX];
    int rt,L;
    int newnode()
    {
        for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
        e[L++]=0;
        return L-1;
    }
    void init(){L=0;rt=newnode();}
    void insert(char *buf)
    {
        int len=strlen(buf),now=rt;
        for(int i=0;i<len;++i)
        {
            int x=buf[i]-'a';
            if(nxt[now][x]==-1) nxt[now][x]=newnode();
            now=nxt[now][x];
        }
        ++e[now];
    }
    void build()
    {
        queue<int>q;
        f[rt]=rt;
        for(int i=0;i<MAXN;++i)
        if(nxt[rt][i]==-1) nxt[rt][i]=rt;
        else
        {
            f[nxt[rt][i]]=rt;
            q.push(nxt[rt][i]);
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<MAXN;++i)
            if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
            else
            {
                f[nxt[now][i]]=nxt[f[now]][i];
                q.push(nxt[now][i]);
            }
        }
    }
    int query(char *buf)
    {
        int len=strlen(buf),now=rt,res=0;
        for(int i=0;i<len;++i)
        {
            now=nxt[now][buf[i]-'a'];
            int tmp=now;
            while(tmp!=rt)
            {
                res+=e[tmp];
                e[tmp]=0;
                tmp=f[tmp];
            }
        }
        return res;
    }
};
char s[MAX];
int n,t;
Trie AC;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        AC.init();
        scanf("%d",&n);
        for(int i=0;i<n;++i)
        {
            scanf("%s",s);
            AC.insert(s);
        }
        AC.build();
        scanf("%s",s);
        printf("%d\n",AC.query(s));
    }
    return 0;
}

HDU2896 病毒侵袭

给定N个病毒串M个匹配串,对于每个匹配串查询其子串

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+5;
struct Trie
{
    static const int MAXN=128;
    int nxt[MAX][MAXN],f[MAX],e[MAX];
    int rt,L;
    int newnode()
    {
        for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
        e[L++]=0;
        return L-1;
    }
    void init(){L=0;rt=newnode();}
    void insert(char *buf,int id)
    {
        int len=strlen(buf),now=rt;
        for(int i=0;i<len;++i)
        {
            int x=buf[i];
            if(nxt[now][x]==-1) nxt[now][x]=newnode();
            now=nxt[now][x];
        }
        e[now]=id;
    }
    void build()
    {
        queue<int>q;
        f[rt]=rt;
        for(int i=0;i<MAXN;++i)
        if(nxt[rt][i]==-1) nxt[rt][i]=rt;
        else
        {
            f[nxt[rt][i]]=rt;
            q.push(nxt[rt][i]);
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<MAXN;++i)
            if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
            else
            {
                f[nxt[now][i]]=nxt[f[now]][i];
                q.push(nxt[now][i]);
            }
        }
    }
    void query(char *buf,set<int>&stt)
    {
        int len=strlen(buf),now=rt;
        for(int i=0;i<len;++i)
        {
            now=nxt[now][buf[i]];
            int tmp=now;
            while(tmp!=rt)
            {
                if(e[tmp]!=0) stt.insert(e[tmp]);
                tmp=f[tmp];
            }
        }
    }
};
char s[MAX];
int n,m;
Trie AC;
set<int>st;
set<int>::iterator p;
int main()
{
    AC.init();
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s);
        AC.insert(s,i);
    }
    AC.build();
    scanf("%d",&m);
    int ans=0;
    for(int i=1;i<=m;++i)
    {
        scanf("%s",s);
        st.clear();
        AC.query(s,st);
        if(!st.empty())
        {
            printf("web %d:",i);
            for(p=st.begin();p!=st.end();++p) printf(" %d",*p);
            printf("\n");++ans;
        }
    }
    printf("total: %d\n",ans);
    return 0;
}

POJ2778 DNA Sequence

给定n个病毒序列,求长度为m的健康序列的种数

AC自动机跑有向图邻接矩阵,求根节点可到达的非病毒终点的位置,矩阵的k次为走k步可到达的位置

e记录病毒终点,以及f指针指向病毒终点的点(该串的子串是病毒串,例如健康串abc,病毒串bc,b->b,c->c,其中c是病毒终点)

答案为根节点所在的行值相加

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAX=105;
const int MOD=1e5;
int L=0;
inline int id(char c)
{
    if(c=='A') return 0;
    if(c=='C') return 1;
    if(c=='T') return 2;
    return 3;
}
struct Mat
{
    long long m[MAX][MAX];
    Mat(){memset(m,0,sizeof(m));}
};
Mat multi(const Mat &a,const Mat &b)
{
    Mat c;
    for(int i=0;i<L;i++)
    for(int j=0;j<L;j++)if(a.m[j][i]!=0)
    for(int k=0;k<L;k++)if(b.m[i][k]!=0)
    c.m[j][k]=(c.m[j][k]+a.m[j][i]*b.m[i][k]%MOD)%MOD;
    return c;
}
Mat pow(Mat &a,int k)
{
    Mat b;
    for(int i=0;i<L;i++) b.m[i][i]=1;
    while(k)
    {
        if(k&1) b=multi(b,a);
        a=multi(a,a);
        k>>=1;
    }
    return b;
}
int q[MAX],l,r;
struct Trie
{
    static const int MAXN=4;
    int nxt[MAX][MAXN],f[MAX],e[MAX];
    int rt;
    int newnode()
    {
        for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
        e[L++]=0;
        return L-1;
    }
    void init(){L=0;rt=newnode();}
    void insert(char *buf)
    {
        int len=strlen(buf),now=rt;
        for(int i=0;i<len;++i)
        {
            int x=id(buf[i]);
            if(nxt[now][x]==-1) nxt[now][x]=newnode();
            now=nxt[now][x];
        }
        e[now]=1;
    }
    void build()
    {
        l=0,r=-1;
        f[rt]=rt;
        for(int i=0;i<MAXN;++i)
        if(nxt[rt][i]==-1) nxt[rt][i]=rt;
        else
        {
            f[nxt[rt][i]]=rt;
            q[++r]=nxt[rt][i];
        }
        while(l<=r)
        {
            int now=q[l++];
            for(int i=0;i<MAXN;++i)
            if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
            else
            {
                f[nxt[now][i]]=nxt[f[now]][i];
                q[++r]=nxt[now][i];
                e[nxt[now][i]]|=e[f[nxt[now][i]]];
            }
        }
    }
    void get(Mat &a)
    {
        for(int i=0;i<L;++i)if(!e[i])
        for(int j=0;j<MAXN;++j)if(!e[nxt[i][j]])
        a.m[i][nxt[i][j]]=(a.m[i][nxt[i][j]]+1)%MOD;
    }
};
char s[MAX];
int n,m;
Trie AC;
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        AC.init();
        for(int i=0;i<n;++i)
        {
            scanf("%s",s);
            AC.insert(s);
        }
        AC.build();
        Mat a;AC.get(a);
        a=pow(a,m);
        long long sum=0;
        for(int i=0;i<L;++i) sum=(sum+a.m[0][i])%MOD;
        printf("%lld\n",sum);
    }
    return 0;
}

HDU2825 Wireless Password

给定m个子串,求组成长度为n的由K个以上子串组成的匹配串的方案数

dp[i][j][k],i记录串的长度,j记录到达的节点,k记录目前走过的完整串的状态

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAX=105;
const int MOD=20090717;
struct Trie
{
    static const int MAXN=26;
    int nxt[MAX][MAXN],f[MAX],e[MAX];
    int rt,L;
    int newnode()
    {
        for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
        e[L++]=0;
        return L-1;
    }
    void init(){L=0;rt=newnode();}
    void insert(char *buf,int id)
    {
        int len=strlen(buf),now=rt;
        for(int i=0;i<len;++i)
        {
            int x=buf[i]-'a';
            if(nxt[now][x]==-1) nxt[now][x]=newnode();
            now=nxt[now][x];
        }
        e[now]|=1<<id;
    }
    void build()
    {
        queue<int>q;
        f[rt]=rt;
        for(int i=0;i<MAXN;++i)
        if(nxt[rt][i]==-1) nxt[rt][i]=rt;
        else
        {
            f[nxt[rt][i]]=rt;
            q.push(nxt[rt][i]);
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<MAXN;++i)
            if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
            else
            {
                f[nxt[now][i]]=nxt[f[now]][i];
                q.push(nxt[now][i]);
                e[nxt[now][i]]|=e[f[nxt[now][i]]];
            }
        }
    }
}AC;
char s[MAX];
int n,m,K,cnt[1<<10];
long long dp[26][MAX][1<<10];
int main()
{
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<(1<<10);++i)
    for(int j=0;j<10;++j)if(i&(1<<j))
    ++cnt[i];
    while(~scanf("%d%d%d",&n,&m,&K))
    {
        if(n==0&&m==0&&K==0) break;
        AC.init();
        for(int i=0;i<m;++i)
        {
            scanf("%s",s);
            AC.insert(s,i);
        }
        AC.build();
        for(int i=0;i<=n;++i)
        for(int j=0;j<AC.L;++j)
        for(int k=0;k<(1<<m);++k)
        dp[i][j][k]=0;
        dp[0][0][0]=1;
        for(int i=0;i<n;++i)
        for(int j=0;j<AC.L;++j)
        for(int k=0;k<(1<<m);++k) if(dp[i][j][k]>0)
        for(int p=0;p<AC.MAXN;++p)
        {
            int xi=i+1,xj=AC.nxt[j][p],xk=k|AC.e[xj];
            dp[xi][xj][xk]=(dp[xi][xj][xk]+dp[i][j][k])%MOD;
        }
        long long ans=0;
        for(int k=0;k<(1<<m);++k)if(cnt[k]>=K)
        for(int j=0;j<AC.L;++j)
        ans=(ans+dp[n][j][k])%MOD;
        printf("%lld\n",ans);
    }
    return 0;
}

HDU2296 Ring

给定子串及其价值,求由这些串组成的长度为n价值最大字典序最小的串

dp[i][j],i表示串的长度,j表示到达的节点

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAX=1e3+1e2+5;
const int INF=0x3f3f3f3f;
struct Trie
{
    static const int MAXN=26;
    int nxt[MAX][MAXN],f[MAX],e[MAX];
    int rt,L;
    int newnode()
    {
        for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
        e[L++]=0;
        return L-1;
    }
    void init(){L=0;rt=newnode();}
    void insert(char *buf,int id)
    {
        int len=strlen(buf),now=rt;
        for(int i=0;i<len;++i)
        {
            int x=buf[i]-'a';
            if(nxt[now][x]==-1) nxt[now][x]=newnode();
            now=nxt[now][x];
        }
        e[now]=id;
    }
    void build()
    {
        queue<int>q;
        f[rt]=rt;
        for(int i=0;i<MAXN;++i)
        if(nxt[rt][i]==-1) nxt[rt][i]=rt;
        else
        {
            f[nxt[rt][i]]=rt;
            q.push(nxt[rt][i]);
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<MAXN;++i)
            if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
            else
            {
                f[nxt[now][i]]=nxt[f[now]][i];
                q.push(nxt[now][i]);
            }
        }
    }
}AC;
bool cmp(char *a,char *b)
{
    int al=strlen(a),bl=strlen(b);
    return al==bl?strcmp(a,b)<0:al<bl;
}
char s[15],str[55][MAX][55],tmp[55],ans[55];
int n,m,t,val[105],dp[55][MAX];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        AC.init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;++i)
        {
            scanf("%s",s);
            AC.insert(s,i);
        }
        for(int i=1;i<=m;++i) scanf("%d",&val[i]);
        AC.build();
        for(int i=0;i<=n;++i)
        for(int j=0;j<AC.L;++j)
        dp[i][j]=-INF;
        dp[0][0]=0;
        strcpy(str[0][0],"");strcpy(ans,"");
        int max_=0;
        for(int i=0;i<n;++i)
        for(int j=0;j<AC.L;++j)if(dp[i][j]>=0)
        {
            strcpy(tmp,str[i][j]);
            int len=strlen(tmp);
            for(int k=0;k<AC.MAXN;++k)
            {
                int xi=i+1,xj=AC.nxt[j][k],t=dp[i][j];
                tmp[len]='a'+k;
                tmp[len+1]='\0';
                if(AC.e[xj]) t+=val[AC.e[xj]];
                if(dp[xi][xj]<t||(dp[xi][xj]==t&&cmp(tmp,str[xi][xj])))
                {
                    dp[xi][xj]=t;
                    strcpy(str[xi][xj],tmp);
                    if(max_<t||(max_==t&&cmp(tmp,ans)))
                    {
                        max_=t;
                        strcpy(ans,tmp);
                    }
                }
            }
        }
        printf("%s\n",ans);
    }
    return 0;
}

HDU3341 Lost's revenge

给定n个子串,求一个串经过重排所能包含的最多字串的个数

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAX=505;
const int INF=0x3f3f3f3f;
int id(char c)
{
    if(c=='A') return 0;
    if(c=='C') return 1;
    if(c=='G') return 2;
    else return 3;
}
struct Trie
{
    static const int MAXN=4;
    int nxt[MAX][MAXN],f[MAX],e[MAX];
    int rt,L;
    int newnode()
    {
        for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
        e[L++]=0;
        return L-1;
    }
    void init(){L=0;rt=newnode();}
    void insert(char *buf)
    {
        int len=strlen(buf),now=rt;
        for(int i=0;i<len;++i)
        {
            int x=id(buf[i]);
            if(nxt[now][x]==-1) nxt[now][x]=newnode();
            now=nxt[now][x];
        }
        ++e[now];
    }
    void build()
    {
        queue<int>q;
        f[rt]=rt;
        for(int i=0;i<MAXN;++i)
        if(nxt[rt][i]==-1) nxt[rt][i]=rt;
        else
        {
            f[nxt[rt][i]]=rt;
            q.push(nxt[rt][i]);
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<MAXN;++i)
            if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
            else
            {
                f[nxt[now][i]]=nxt[f[now]][i];
                q.push(nxt[now][i]);
                e[nxt[now][i]]+=e[f[nxt[now][i]]];
            }
        }
    }
}AC;
char s[1005];
int n,cnt[4],bit[4],dp[MAX][20005];
int main()
{
    int tc=1;
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        AC.init();
        for(int i=0;i<n;++i)
        {
            scanf("%s",s);
            AC.insert(s);
        }
        AC.build();
        scanf("%s",s);
        int len=strlen(s);
        memset(cnt,0,sizeof(cnt));
        memset(bit,0,sizeof(bit));
        memset(dp,-INF,sizeof(dp));
        for(int i=0;i<len;++i) ++cnt[id(s[i])];
        bit[3]=1;bit[2]=cnt[3]+1;bit[1]=bit[2]*(cnt[2]+1);bit[0]=bit[1]*(cnt[1]+1);
        dp[0][0]=0;
        for(int a=0;a<=cnt[0];++a)
        for(int c=0;c<=cnt[1];++c)
        for(int g=0;g<=cnt[2];++g)
        for(int t=0;t<=cnt[3];++t)
        {
            int ha=a*bit[0]+c*bit[1]+g*bit[2]+t*bit[3];
            for(int i=0;i<AC.L;++i)if(dp[i][ha]>=0)
            for(int j=0;j<AC.MAXN;++j)
            {
                if(j==0&&a==cnt[0]) continue;
                if(j==1&&c==cnt[1]) continue;
                if(j==2&&g==cnt[2]) continue;
                if(j==3&&t==cnt[3]) continue;
                int xi=AC.nxt[i][j],xha=ha+bit[j];
                dp[xi][xha]=max(dp[xi][xha],dp[i][ha]+AC.e[xi]);
            }
        }
        int ha=cnt[0]*bit[0]+cnt[1]*bit[1]+cnt[2]*bit[2]+cnt[3]*bit[3];
        int ans=0;
        for(int i=0;i<AC.L;++i) ans=max(ans,dp[i][ha]);
        printf("Case %d: %d\n",tc++,ans);
    }
    return 0;
}

HDU3247 Resource Archiver

给定n个好串,m个坏串,求最短的走遍好串不走坏串的最短串的长度

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAX=2e6;
const int INF=0x3f3f3f3f;
int dis[MAX],pos[11],g[11][11],cnt;
struct Trie
{
    static const int MAXN=2;
    int nxt[MAX][MAXN],f[MAX],e[MAX];
    int rt,L;
    int newnode()
    {
        for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
        e[L++]=0;
        return L-1;
    }
    void init(){L=0;rt=newnode();}
    void insert(char *buf,int v)
    {
        int len=strlen(buf),now=rt;
        for(int i=0;i<len;++i)
        {
            int x=buf[i]-'0';
            if(nxt[now][x]==-1) nxt[now][x]=newnode();
            now=nxt[now][x];
        }
        e[now]=v;
    }
    void build()
    {
        queue<int>q;
        f[rt]=rt;
        for(int i=0;i<MAXN;++i)
        if(nxt[rt][i]==-1) nxt[rt][i]=rt;
        else
        {
            f[nxt[rt][i]]=rt;
            q.push(nxt[rt][i]);
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<MAXN;++i)
            if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
            else
            {
                f[nxt[now][i]]=nxt[f[now]][i];
                q.push(nxt[now][i]);
                if(e[f[nxt[now][i]]]==-1) e[nxt[now][i]]=-1;
            }
        }
    }
    void spfa(int k)
    {
        queue<int>q;
        memset(dis,-1,sizeof(dis));
        dis[pos[k]]=0;
        q.push(pos[k]);
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<2;++i)
            {
                int tmp=nxt[now][i];
                if(dis[tmp]<0&&e[tmp]>=0)
                {
                    dis[tmp]=dis[now]+1;
                    q.push(tmp);
                }
            }
        }
        for(int i=0;i<cnt;++i) g[k][i]=dis[pos[i]];
    }
}AC;
char s[1005];
int n,m,dp[1005][11];
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        AC.init();
        for(int i=0;i<n;++i) {scanf("%s",s);AC.insert(s,1<<i);}
        for(int i=0;i<m;++i) {scanf("%s",s);AC.insert(s,-1);}
        AC.build();
        pos[0]=0;cnt=1;
        for(int i=0;i<AC.L;++i)if(AC.e[i]>0) pos[cnt++]=i;
        for(int i=0;i<cnt;++i) AC.spfa(i);
        for(int i=0;i<(1<<n);++i)
        for(int j=0;j<cnt;++j)
        dp[i][j]=INF;
        dp[0][0]=0;
        for(int i=0;i<(1<<n);++i)
        for(int j=0;j<cnt;++j)if(dp[i][j]<INF)
        for(int k=0;k<cnt;++k)if(g[j][k]>=0&&j!=k)
        dp[i|AC.e[pos[k]]][k]=min(dp[i|AC.e[pos[k]]][k],dp[i][j]+g[j][k]);
        int ans=INF;
        for(int i=0;i<cnt;++i) ans=min(ans,dp[(1<<n)-1][i]);
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Nrtostp/article/details/82859744