AC自动机小结

  

    AC自动机在trie树上实现KMP的一种数据结构,可以完成多模式串的匹配,核心要理解fail指针的含义,即让当前字符失配时跳转到具有最长公共前后缀的字符继续匹配,从根节点到当前节点(s)fail指针的节点(p)的路径字符串必定为从根节点到节点s的路径字符串的一个后缀,还有理解trie图,当字符串在trie树上行走没有路可走时,fail指针指向的节点可相当于字符串要走的下一点,然后再无限匹配下去,具体介绍看trie图的构建、活用与理解

学习资料:http://blog.csdn.net/niushuai666/article/details/7002823

              http://hi.baidu.com/nialv7/item/ce1ce015d44a6ba7feded52d

模板题

1.HDU 2222

题意:统计目标串中模式串的个数。

#include <bits/stdc++.h>

using namespace std;

const int N = 500010;

struct Trie

{

    int ch[N][26],fail[N],last[N];

    int root,sz;

    int newnode()

    {

        for(int i=0;i<26;i++)ch[sz][i]=-1;

        last[sz++]=0;

        return sz-1;

    }

    void init()

    {

        sz=0;

        root=newnode();

    }

    void insert(char s[])

    {

        int len=strlen(s);

        int now=root;

        for(int i=0;i<len;i++)

        {

            int id=s[i]-'a';

            if(ch[now][id]==-1)

                ch[now][id]=newnode();

            now=ch[now][id];

        }

        last[now]++;

    }

    void build()

    {

        queue<int>que;

        fail[root]=root;

        for(int i=0;i<26;i++)

        {

            if(ch[root][i]==-1)ch[root][i]=root;

            else

            {

                fail[ch[root][i]]=root;

                que.push(ch[root][i]);

            }

        }

        while(!que.empty())

        {

            int now=que.front();que.pop();

            for(int i=0;i<26;i++)

            {

                if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];

                else

                {

                    fail[ch[now][i]]=ch[fail[now]][i];

                    que.push(ch[now][i]);

                }

            }

        }

    }

    int query(char s[])

    {

        int len=strlen(s);

        int now=root,res=0;

        for(int i=0;i<len;i++)

        {

            int id=s[i]-'a';

            now=ch[now][id];

            int temp=now;

            while(temp!=root)

            {

                res+=last[temp];

                last[temp]=0;

                temp=fail[temp];

            }

        }

        return res;

    }

}ac;

char str[1000010];

int main()

{

    int T,n;

    scanf("%d",&T);

    while(T--)

    {

        scanf("%d",&n);

        ac.init();

        for(int i=0;i<n;i++)

        {

            scanf("%s",str);

            ac.insert(str);

        }

        ac.build();

        scanf("%s",str);

        printf("%d\n",ac.query(str));

    }

}
View Code

2.HDU 2896

题意:统计每个目标串中模式串的个数和id。

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;

struct Trie

{

    int ch[N][128],fail[N],last[N];

    int root,sz;

    int newnode()

    {

        for(int i=0;i<128;i++)ch[sz][i]=-1;

        last[sz++]=0;

        return sz-1;

    }

    void init()

    {

        sz=0;

        root=newnode();

    }

    void insert(char s[],int num)

    {

        int len=strlen(s);

        int now=root;

        for(int i=0;i<len;i++)

        {

            int id=s[i];

            if(ch[now][id]==-1)

                ch[now][id]=newnode();

            now=ch[now][id];

        }

        last[now]=num;

    }

    void build()

    {

        queue<int>que;

        fail[root]=root;

        for(int i=0;i<128;i++)

        {

            if(ch[root][i]==-1)ch[root][i]=root;

            else

            {

                fail[ch[root][i]]=root;

                que.push(ch[root][i]);

            }

        }

        while(!que.empty())

        {

            int now=que.front();que.pop();

            for(int i=0;i<128;i++)

            {

                if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];

                else

                {

                    fail[ch[now][i]]=ch[fail[now]][i];

                    que.push(ch[now][i]);

                }

            }

        }

    }

    bool vis[510];

    bool query(char s[],int n,int num)

    {

        int len=strlen(s);

        int now=root;

        bool flag=false;

        memset(vis,false,sizeof(vis));

        for(int i=0;i<len;i++)

        {

            int id=s[i];

            now=ch[now][id];

            int temp=now;

            while(temp!=root)

            {

                if(last[temp])

                {

                    vis[last[temp]]=true;

                    flag=true;

                }

                temp=fail[temp];

            }

        }

        if(!flag)return false;

        printf("web %d:",num);

        for(int i=1;i<=n;i++)

            if(vis[i])printf(" %d",i);

        puts("");

        return true;

    }

}ac;

char str[10010];

int main()

{

    int n,m;

    while(scanf("%d",&n)>0)

    {

        ac.init();

        for(int i=1;i<=n;i++)

        {

            scanf("%s",str);

            ac.insert(str,i);

        }

        ac.build();

        int ans=0;

        scanf("%d",&m);

        for(int i=1;i<=m;i++)

        {

            scanf("%s",str);

            if(ac.query(str,n,i))ans++;

        }

        printf("total: %d\n",ans);

    }

}
View Code

3.HDU 3065

题意:统计每个模式串在目标串中出现的次数。

#include <bits/stdc++.h>

using namespace std;

const int N = 50010;

char str[1010][110];

struct Trie

{

    int ch[N][128],fail[N],last[N];

    int root,sz;

    int newnode()

    {

        for(int i=0;i<128;i++)ch[sz][i]=-1;

        last[sz++]=0;

        return sz-1;

    }

    void init()

    {

        sz=0;

        root=newnode();

    }

    void insert(char s[],int num)

    {

        int len=strlen(s);

        int now=root;

        for(int i=0;i<len;i++)

        {

            int id=s[i];

            if(ch[now][id]==-1)

                ch[now][id]=newnode();

            now=ch[now][id];

        }

        last[now]=num;

    }

    void build()

    {

        queue<int>que;

        fail[root]=root;

        for(int i=0;i<128;i++)

        {

            if(ch[root][i]==-1)ch[root][i]=root;

            else

            {

                fail[ch[root][i]]=root;

                que.push(ch[root][i]);

            }

        }

        while(!que.empty())

        {

            int now=que.front();que.pop();

            for(int i=0;i<128;i++)

            {

                if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];

                else

                {

                    fail[ch[now][i]]=ch[fail[now]][i];

                    que.push(ch[now][i]);

                }

            }

        }

    }

    int vis[1010];

    void query(char s[],int n)

    {

        int len=strlen(s);

        int now=root;

        memset(vis,0,sizeof(vis));

        for(int i=0;i<len;i++)

        {

            int id=s[i];

            now=ch[now][id];

            int temp=now;

            while(temp!=root)

            {

                if(last[temp])

                {

                    vis[last[temp]]++;

                }

                temp=fail[temp];

            }

        }

        for(int i=1;i<=n;i++)

            if(vis[i])printf("%s: %d\n",str[i],vis[i]);

    }

}ac;

char s[2000010];

int main()

{

    int n,m;

    while(scanf("%d",&n)>0)

    {

        ac.init();

        for(int i=1;i<=n;i++)

        {

            scanf("%s",str[i]);

            ac.insert(str[i],i);

        }

        ac.build();

        scanf("%s",s);

        ac.query(s,n);

    }

}
View Code

4.ZOJ 3228

题意:统计模式串在目标串中出现的次数,一种是可重叠的,一种是不可重叠的。

#include <bits/stdc++.h>

using namespace std;

const int N = 600010;

struct Trie

{

    int ch[N][26],fail[N],len[N];

    int root,sz;

    int newnode()

    {

        for(int i=0;i<26;i++)ch[sz][i]=-1;

        last[sz++]=0;

        return sz-1;

    }

    void init()

    {

        sz=0;

        root=newnode();

    }

    int insert(char s[])

    {

        int lens=strlen(s);

        int now=root;

        for(int i=0;i<lens;i++)

        {

            int id=s[i]-'a';

            if(ch[now][id]==-1)

            {

                ch[now][id]=newnode();

                len[ch[now][id]]=i+1;

            }

            now=ch[now][id];

        }

        return now;

    }

    void build()

    {

        queue<int>que;

        fail[root]=root;

        for(int i=0;i<26;i++)

        {

            if(ch[root][i]==-1)ch[root][i]=root;

            else

            {

                fail[ch[root][i]]=root;

                que.push(ch[root][i]);

            }

        }

        while(!que.empty())

        {

            int now=que.front();que.pop();

            for(int i=0;i<26;i++)

            {

                if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];

                else

                {

                    fail[ch[now][i]]=ch[fail[now]][i];

                    que.push(ch[now][i]);

                }

            }

        }

    }

    int cnt[N][2],last[N];

    void query(char s[])

    {

        int lens=strlen(s);

        int now=root;

        memset(cnt,0,sizeof(cnt));

        memset(last,0,sizeof(last));

        for(int i=0;i<lens;i++)

        {

            int id=s[i]-'a';

            now=ch[now][id];

            int temp=now;

            while(temp!=root)

            {

                cnt[temp][0]++;

                if(i+1-last[temp]>=len[temp])

                {

                    last[temp]=i+1;

                    cnt[temp][1]++;

                }

                temp=fail[temp];

            }

        }

    }

}ac;

char s[N],str[20];

int pos[N],type[N];

int main()

{

    int n,cas=1;

    while(scanf("%s",s)>0)

    {

        ac.init();

        scanf("%d",&n);

        for(int i=0;i<n;i++)

        {

            scanf("%d%s",&type[i],str);

            pos[i]=ac.insert(str);

        }

        ac.build();

        ac.query(s);

        printf("Case %d\n",cas++);

        for(int i=0;i<n;i++)printf("%d\n",ac.cnt[pos[i]][type[i]]);

        puts("");

    }

}
View Code

矩阵

1.POJ 2778

题意:有m种DNA序列是有病毒的,问有多少种长度为n的DNA序列不包含任何一种有病毒的DNA序列。

分析:在trie图上走n步不包含病毒的节点,建立好矩阵进行状态转移,和有向图中走n步从一点到另一点一样,直接矩阵快速幂。

#include <cstdio>

#include <cstring>

#include <queue>

#include <algorithm>

using namespace std;

const int N = 110;

const int mod = 100000;

struct matrix

{

    int m[N][N],n;

    matrix(){}

    matrix(int _n)

    {

        n=_n;

        for(int i=0;i<n;i++)

            for(int j=0;j<n;j++)

            m[i][j]=0;

    }

    matrix operator*(const matrix &a)const

    {

        matrix res=matrix(n);

        for(int k=0;k<n;k++)

        for(int i=0;i<n;i++)

        for(int j=0;j<n;j++)

        {

            res.m[i][j]=(res.m[i][j]+1ll*m[i][k]*a.m[k][j])%mod;

        }

        return res;

    }

};

struct Trie

{

    int ch[N][4],fail[N];

    bool last[N];

    int root,sz;

    int newnode()

    {

        for(int i=0;i<4;i++)ch[sz][i]=-1;

        last[sz++]=false;

        return sz-1;

    }

    void init()

    {

        sz=0;

        root=newnode();

    }

    int getid(char ch)

    {

        if(ch=='A')return 0;

        else if(ch=='C')return 1;

        else if(ch=='G')return 2;

        else return 3;

    }

    void insert(char s[])

    {

        int lens=strlen(s);

        int now=root;

        for(int i=0;i<lens;i++)

        {

            int id=getid(s[i]);

            if(ch[now][id]==-1)

            {

                ch[now][id]=newnode();

            }

            now=ch[now][id];

        }

        last[now]=true;

    }

    void build()

    {

        queue<int>que;

        fail[root]=root;

        for(int i=0;i<4;i++)

        {

            if(ch[root][i]==-1)ch[root][i]=root;

            else

            {

                fail[ch[root][i]]=root;

                que.push(ch[root][i]);

            }

        }

        while(!que.empty())

        {

            int now=que.front();que.pop();

            if(last[fail[now]])last[now]=true;

            for(int i=0;i<4;i++)

            {

                if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];

                else

                {

                    fail[ch[now][i]]=ch[fail[now]][i];

                    que.push(ch[now][i]);

                }

            }

        }

    }

    matrix getmat()

    {

        matrix res=matrix(sz);

        for(int i=0;i<sz;i++)

            for(int j=0;j<4;j++)

            if(!last[ch[i][j]])res.m[i][ch[i][j]]++;

        return res;

    }

}ac;

matrix quick_mod(matrix a,int n)

{

    matrix res=matrix(ac.sz);

    for(int i=0;i<ac.sz;i++)res.m[i][i]=1;

    while(n)

    {

        if(n&1)res=res*a;

        a=a*a;

        n>>=1;

    }

    return res;

}

int main()

{

    int n,m;

    while(scanf("%d%d",&n,&m)>0)

    {

        ac.init();

        for(int i=1;i<=n;i++)

        {

            char s[15];

            scanf("%s",s);

            ac.insert(s);

        }

        ac.build();

        matrix a=ac.getmat();

        a=quick_mod(a,m);

        int ans=0;

        for(int i=0;i<ac.sz;i++)

        {

            ans+=a.m[0][i];

        }

        ans%=mod;

        printf("%d\n",ans);

    }

}
View Code

2.HDU 2243

题意:给你n 个单词,求出满足以下条件的单词个数:长度不大于L 且单词中至少包含一个子串为前面n 个单词中任意一个。

分析:先求出所有单词的数量 26^1+ 26^2+ ... + 26^L,可以构造矩阵乘法求出。然后求出所有不包含 n 个单词的串的数量,两者相减就是答案了。

#include <cstdio>

#include <cstring>

#include <queue>

#include <algorithm>

using namespace std;

typedef unsigned long long ull;

const int N = 50;

const int mod = 100000;

struct matrix

{

    ull m[N*2][N*2],n;

    matrix(){}

    matrix(int _n)

    {

        n=_n;

        for(int i=0;i<n;i++)

            for(int j=0;j<n;j++)

            m[i][j]=0;

    }

    matrix operator*(const matrix &a)const

    {

        matrix res=matrix(n);

        for(int k=0;k<n;k++)

        for(int i=0;i<n;i++)

        for(int j=0;j<n;j++)

        {

            res.m[i][j]=res.m[i][j]+m[i][k]*a.m[k][j];

        }

        return res;

    }

};

struct Trie

{

    int ch[N][26],fail[N];

    bool last[N];

    int root,sz;

    int newnode()

    {

        for(int i=0;i<26;i++)ch[sz][i]=-1;

        last[sz++]=false;

        return sz-1;

    }

    void init()

    {

        sz=0;

        root=newnode();

    }

    void insert(char s[])

    {

        int lens=strlen(s);

        int now=root;

        for(int i=0;i<lens;i++)

        {

            int id=s[i]-'a';

            if(ch[now][id]==-1)

            {

                ch[now][id]=newnode();

            }

            now=ch[now][id];

        }

        last[now]=true;

    }

    void build()

    {

        queue<int>que;

        fail[root]=root;

        for(int i=0;i<26;i++)

        {

            if(ch[root][i]==-1)ch[root][i]=root;

            else

            {

                fail[ch[root][i]]=root;

                que.push(ch[root][i]);

            }

        }

        while(!que.empty())

        {

            int now=que.front();que.pop();

            if(last[fail[now]])last[now]=true;

            for(int i=0;i<26;i++)

            {

                if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];

                else

                {

                    fail[ch[now][i]]=ch[fail[now]][i];

                    que.push(ch[now][i]);

                }

            }

        }

    }

    matrix getmat()

    {

        matrix res=matrix(sz*2);

        for(int i=0;i<sz;i++)

            for(int j=0;j<26;j++)

            if(!last[ch[i][j]])res.m[i][ch[i][j]]++;

        for(int i=0;i<sz;i++)res.m[i][i+sz]=res.m[i+sz][i+sz]=1;

        return res;

    }

}ac;

matrix quick_mod(matrix a,int n)

{

    matrix res=matrix(a.n);

    for(int i=0;i<a.n;i++)res.m[i][i]=1;

    while(n)

    {

        if(n&1)res=res*a;

        a=a*a;

        n>>=1;

    }

    return res;

}

int main()

{

    int n,m;

    char s[15];

    while(scanf("%d%d",&n,&m)>0)

    {

        ac.init();

        for(int i=1;i<=n;i++)

        {

            scanf("%s",s);

            ac.insert(s);

        }

        ac.build();

        matrix a=ac.getmat();

        a=quick_mod(a,m);

        ull ans1=0;

        for(int i=0;i<ac.sz*2;i++)

        {

            ans1+=a.m[0][i];

        }

        ans1--;

        matrix b=matrix(2);

        b.m[0][0]=26;b.m[0][1]=1;

        b.m[1][0]=0;b.m[1][1]=1;

        b=quick_mod(b,m);

        ull ans2=0;

        ans2=b.m[0][0]+b.m[0][1];

        ans2--;

        printf("%I64u\n",ans2-ans1);

    }

}
View Code

DP

1.POJ 1625

题意:给出病毒串,求长度为n且不含病毒串的DNA种数。

分析:dp[i][j]表示长度为i时,以自动机上结点编号为j结尾的种数。dp[i][j]+=dp[i-1][k],k是自动机上能转移到j的结点,且j、k都不是病毒串的结尾。(dp[0][0]=1)

#include <cstdio>

#include <cstring>

#include <queue>

#include <algorithm>

#include <map>

using namespace std;

const int N = 110;

const int mod = 100000;

map<char,int>mp;

struct BigInt

{

    const static int mod = 10000;

    const static int DLEN = 4;

    int a[600],len;

    BigInt()

    {

        memset(a,0,sizeof(a));

        len = 1;

    }

    BigInt(int v)

    {

        memset(a,0,sizeof(a));

        len = 0;

        do

        {

            a[len++] = v%mod;

            v /= mod;

        }while(v);

    }

    BigInt(const char s[])

    {

        memset(a,0,sizeof(a));

        int L = strlen(s);

        len = L/DLEN;

        if(L%DLEN)len++;

        int index = 0;

        for(int i = L-1;i >= 0;i -= DLEN)

        {

            int t = 0;

            int k = i - DLEN + 1;

            if(k < 0)k = 0;

            for(int j = k;j <= i;j++)

                t = t*10 + s[j] - '0';

            a[index++] = t;

        }

    }

    BigInt operator +(const BigInt &b)const

    {

        BigInt res;

        res.len = max(len,b.len);

        for(int i = 0;i <= res.len;i++)

            res.a[i] = 0;

        for(int i = 0;i < res.len;i++)

        {

            res.a[i] += ((i < len)?a[i]:0)+((i < b.len)?b.a[i]:0);

            res.a[i+1] += res.a[i]/mod;

            res.a[i] %= mod;

        }

        if(res.a[res.len] > 0)res.len++;

        return res;

    }

    BigInt operator *(const BigInt &b)const

    {

        BigInt res;

        for(int i = 0; i < len;i++)

        {

            int up = 0;

            for(int j = 0;j < b.len;j++)

            {

                int temp = a[i]*b.a[j] + res.a[i+j] + up;

                res.a[i+j] = temp%mod;

                up = temp/mod;

            }

            if(up != 0)

                res.a[i + b.len] = up;

        }

        res.len = len + b.len;

        while(res.a[res.len - 1] == 0 &&res.len > 1)res.len--;

        return res;

    }

    void output()

    {

        printf("%d",a[len-1]);

        for(int i = len-2;i >=0 ;i--)

            printf("%04d",a[i]);

        printf("\n");

    }

};

struct matrix

{

    int m[N][N];

    int n;

    matrix(){}

    matrix(int _n)

    {

        n=_n;

        for(int i=0;i<n;i++)

            for(int j=0;j<n;j++)

            m[i][j]=0;

    }

};

struct Trie

{

    int ch[N][256],fail[N];

    bool last[N];

    int root,sz;

    int newnode()

    {

        for(int i=0;i<256;i++)ch[sz][i]=-1;

        last[sz++]=false;

        return sz-1;

    }

    void init()

    {

        sz=0;

        root=newnode();

    }

    void insert(char s[])

    {

        int lens=strlen(s);

        int now=root;

        for(int i=0;i<lens;i++)

        {

            int id=mp[s[i]];

            if(ch[now][id]==-1)

            {

                ch[now][id]=newnode();

            }

            now=ch[now][id];

        }

        last[now]=true;

    }

    void build()

    {

        queue<int>que;

        fail[root]=root;

        for(int i=0;i<256;i++)

        {

            if(ch[root][i]==-1)ch[root][i]=root;

            else

            {

                fail[ch[root][i]]=root;

                que.push(ch[root][i]);

            }

        }

        while(!que.empty())

        {

            int now=que.front();que.pop();

            if(last[fail[now]])last[now]=true;

            for(int i=0;i<256;i++)

            {

                if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];

                else

                {

                    fail[ch[now][i]]=ch[fail[now]][i];

                    que.push(ch[now][i]);

                }

            }

        }

    }

    matrix getmat(int n)

    {

        matrix res=matrix(sz);

        for(int i=0;i<sz;i++)

            for(int j=0;j<n;j++)

            if(!last[ch[i][j]])res.m[i][ch[i][j]]++;

        return res;

    }

}ac;



char buf[1010];

BigInt dp[2][110];

int main()

{

    int n,m,p;

    while(scanf("%d%d%d",&n,&m,&p)>0)

    {

        getchar();

        gets(buf);

        mp.clear();

        int len = strlen(buf);

        for(int i = 0;i < len;i++)

            mp[buf[i]]=i;

        ac.init();

        for(int i = 0;i < p;i++)

        {

            gets(buf);

            ac.insert(buf);

        }

        ac.build();

        matrix a= ac.getmat(n);

        int now=0;

        dp[now][0]=1;

        for(int i=1;i<a.n;i++)dp[now][i]=0;

        for(int i=1;i<=m;i++)

        {

            now^=1;

            for(int j=0;j<a.n;j++)dp[now][j]=0;

            for(int j=0;j<a.n;j++)

                for(int k=0;k<a.n;k++)

                if(a.m[j][k])dp[now][k]=dp[now][k]+dp[now^1][j]*a.m[j][k];

        }

        BigInt ans = 0;

        for(int i = 0;i < a.n;i++)

            ans = ans + dp[now][i];

        ans.output();

    }

    return 0;

}
View Code

2.HDU 2825

题意:给出字符串(<10),求至少由x个字符串组成的长度为n的字符串种数。

分析:dp[i][j][k]表示长度为i时,以自动机上结点编号为j结尾,把用到的串二进制压缩为k的种数。dp[i][j][k]+=dp[i-1][p][t],p是自动机上能转移到j的结点。(dp[0][0][0]=1)

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <queue>

#include <vector>

using namespace std;

const int mod = 20090717;

const int maxn = 103;

const int max_num = 26;

int idx[256];

int n, m, x;

int dp[26][103][1026];

bool vis[26][103][1026];

int cnt[1025];

struct node {

    int v, p, zt;

    node(){}

    node(int v, int p, int zt) : v(v), p(p), zt(zt){}

}Q[1000006];

struct AcAuto {

    int val[maxn], f[maxn];

    int ch[maxn][max_num], tot;



    void init() {

        tot = 0;

        new_node();

        int i;

        for(i = 0; i < 26; i++)

            idx['a'+i] = i;

    }

    inline int new_node() {

        memset(ch[tot], 0, sizeof(ch[tot]));

        val[tot] = 0;

        f[tot] = 0;

        return tot++;

    }



    void insert(char *s, int id) {

        int i, j, p = 0;

        for(;*s; s++) {

            int k = idx[*s];

            if(!ch[p][k]) ch[p][k] = new_node();

            p = ch[p][k];

        }

        val[p] |= 1<<id;

    }

    void getfail() {

        int i, j, p = 0;

        int q[maxn];

        int *s = q, *e = q;

        for(i = 0; i < max_num; i++) if(ch[0][i]) *e++ = ch[0][i];

        while(s != e) {

            int u = *s++;

            for(i = 0; i < max_num; i++) {

                int &v = ch[u][i];

                if(!v) { v = ch[f[u]][i]; continue; }

                *e++ = v;

                j = f[u];

                while(j && !ch[j][i]) j = f[j];

                f[v] = ch[j][i];

                val[v] |= val[f[v]];

            }

        }

    }

    void solve() {

        int i, j, k, u;

        int M = (1<<m);

        for(i = 0; i <= n; i++)

            for(k = 0; k < tot; k++)

                for(j = 0; j < M; j++)

                    dp[i][k][j] = 0;

        dp[0][0][0] = 1;



        node *s = Q, *e = Q;

        *e++ = node(0, 0, 0);

        vis[0][0][0] = 1;

        while(s != e) {

            node u = *s++;

            vis[u.v][u.p][u.zt] = 0;

            if(u.v >= n) continue;



            for(i = 0; i < max_num; i++) {

                int p = ch[u.p][i];

                node v = node(u.v+1, p, u.zt|val[p]);



                dp[v.v][v.p][v.zt] += dp[u.v][u.p][u.zt];

                if(dp[v.v][v.p][v.zt] >= mod) dp[v.v][v.p][v.zt] -= mod;



                if(!vis[v.v][v.p][v.zt]) {

                    vis[v.v][v.p][v.zt] = 1;

                    *e++ = v;

                }

            }

        }



        int ans = 0;

        for(i = 0; i < M; i++) {

            if(cnt[i] >= x)

            for(j = 0; j < tot; j++) {

                ans += dp[n][j][i];

                if(ans >= mod) ans -= mod;

            }

        }

        printf("%d\n", ans);

    }

}AC;



char str[13];

int main() {

    int i, j;

    for(i = 0; i < 1024; i++) {

        int c = 0;

        for(j = i; j; j -= (j&-j)) c++;

        cnt[i] = c;

    }

    while( ~scanf("%d%d%d", &n, &m, &x) && (n || m || x)) {

        AC.init();

        for(i = 0; i < m; i++) {

            scanf("%s", str);

            AC.insert(str, i);

        }

        AC.getfail();

        AC.solve();

    }

    return 0;

}
View Code

3.UVALive 6806

题意:给定n个字符的花费代价,m个字符串的获取价值及现有的总费用B,求在费用B范围内构造一个拥有最大价值的字符串(包含几个价值字符串获得多大价值)。

分析:构建trie图获取fail指针时顺便总计到达每个节点得到的总价值,及每个节点都加上它fail指针指向的节点价值,然后进行dp。

dp[i][j]表示到达第i节点时花费j得到最大总价值。

#include <cstdio>

#include <cstring>

#include <queue>

#include <algorithm>

#include <map>

using namespace std;

const int N = 10010;

struct Trie

{

    int ch[N][26],fail[N],val[N];

    int root,sz;

    int newnode()

    {

        for(int i=0; i<26; i++)ch[sz][i]=-1;

        val[sz++]=0;

        return sz-1;

    }

    void init()

    {

        sz=0;

        root=newnode();

    }

    void insert(char s[],int c)

    {

        int len=strlen(s);

        int now=root;

        for(int i=0; i<len; i++)

        {

            int id=s[i]-'A';

            if(ch[now][id]==-1)ch[now][id]=newnode();

            now=ch[now][id];

        }

        val[now]+=c;

    }

    void build()

    {

        queue<int>que;

        fail[root]=root;

        for(int i=0; i<26; i++)

        {

            if(ch[root][i]==-1)ch[root][i]=root;

            else

            {

                fail[ch[root][i]]=root;

                que.push(ch[root][i]);

            }

        }

        while(!que.empty())

        {

            int now=que.front();

            que.pop();

            val[now]+=val[fail[now]];

            for(int i=0; i<26; i++)

            {

                if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];

                else

                {

                    fail[ch[now][i]]=ch[fail[now]][i];

                    que.push(ch[now][i]);

                }

            }

        }

    }

} ac;

char s[110];

int c[30],dp[N][210];

int main()

{

    int T,n,m,b,x,cas=1;

    scanf("%d",&T);

    while(T--)

    {

        scanf("%d%d%d",&n,&m,&b);

        memset(c,0,sizeof(c));

        for(int i=0; i<n; i++)

        {

            scanf("%s%d",s,&x);

            c[s[0]-'A']=x;

        }

        ac.init();

        for(int i=0; i<m; i++)

        {

            scanf("%s%d",s,&x);

            ac.insert(s,x);

        }

        ac.build();

        memset(dp,-1,sizeof(dp));

        dp[0][0]=0;

        int ans=0;

        for(int k=0; k<b; k++)

        {

            for(int i=0; i<ac.sz; i++)

            {

                if(dp[i][k]==-1)continue;

                for(int j=0; j<26; j++)

                {

                    if(!c[j]||k+c[j]>b)continue;

                    int now=ac.ch[i][j],w=c[j];

                    dp[now][k+w]=max(dp[now][k+w],dp[i][k]+ac.val[now]);

                    ans=max(ans,dp[now][k+w]);

                }

            }

        }

        printf("Case #%d: %d\n",cas++,ans);

    }

}
View Code

 

4.HDU 2457

题意:给出病毒串(<50),求最少的修改次数,使得DNA串不含病毒串。

分析:dp[i][j]表示长度为i时,以自动机上结点编号为j结尾,使得该DNA串不含病毒串的最小修改次数。每次转移枚举是否需要修改,以及需要修改的字符。

#include <cstdio>

#include <cstring>

#include <cmath>

#include <queue>

#include <algorithm>

using namespace std;

const int N = 1050;

const int inf = 0x3f3f3f3f;

int dp[N][N];

int n;

struct Trie

{

    int ch[N][4],fail[N];

    bool last[N];

    int root,sz;

    int newnode()

    {

        for(int i=0;i<4;i++)ch[sz][i]=-1;

        last[sz++]=false;

        return sz-1;

    }

    void init()

    {

        sz=0;

        root=newnode();

    }

    int getid(char ch)

    {

        if(ch=='A')return 0;

        else if(ch=='C')return 1;

        else if(ch=='G')return 2;

        else return 3;

    }

    void insert(char s[])

    {

        int len=strlen(s);

        int now=root;

        for(int i=0;i<len;i++)

        {

            int id=getid(s[i]);

            if(ch[now][id]==-1)ch[now][id]=newnode();

            now=ch[now][id];

        }

        last[now]=true;

    }

    void build()

    {

        queue<int>que;

        fail[root]=root;

        for(int i=0;i<4;i++)

        {

            if(ch[root][i]==-1)ch[root][i]=root;

            else

            {

                fail[ch[root][i]]=root;

                que.push(ch[root][i]);

            }

        }

        while(!que.empty())

        {

            int now=que.front();que.pop();

            if(last[fail[now]])last[now]=true;

            for(int i=0;i<4;i++)

            {

                if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];

                else

                {

                    fail[ch[now][i]]=ch[fail[now]][i];

                    que.push(ch[now][i]);

                }

            }

        }

    }

    int solve(char s[])

    {

        int len=strlen(s);

        for(int i=0;i<=len;i++)

            for(int j=0;j<sz;j++)dp[i][j]=inf;

        dp[0][0]=0;

        for(int i=0;i<len;i++)

        {

            int t=getid(s[i]);

            for(int j=0;j<sz;j++)

            {

                if(dp[i][j]>=inf)continue;

                for(int k=0;k<4;k++)

                {

                    int now=ch[j][k];

                    if(last[now])continue;

                    dp[i+1][now]=min(dp[i+1][now],dp[i][j]+(t!=k));

                }

            }

        }

        int ans=inf;

        for(int i=0;i<sz;i++)

            ans=min(ans,dp[len][i]);

        return ans==inf?-1:ans;

    }

}ac;

char s[N];

int main()

{

    int cas=1;

   while(scanf("%d",&n),n)

   {

       ac.init();

       for(int i=0;i<n;i++)

       {

           scanf("%s",s);

           ac.insert(s);

       }

       ac.build();

       scanf("%s",s);

       printf("Case %d: %d\n",cas++,ac.solve(s));

   }

}
View Code

4.HDU 3247

题意:有n(<10)个01串以及m(<1000)个病毒串,将01串拼接成最短的且不含病毒串。

分析:将01串和病毒串一起构造自动机,枚举每个01串的结尾广搜,可以得到该串和其他01串结尾的最短距离。

对n个01串二进制压缩,即TSP,dp[i][j]表示状态为i,最后拼接上的01串是第j个。这题要先排除各个串相互

为子串的情况,否则得到的必定不是最优答案。这题的数据太弱了,网上很多代码都过不了这几组数据也能AC.

Input:
3 1
00000
00000
11111
01
3 2
101
010
1111
001
011
2 2
1110
0111
101
1001
3 3
0001
0000
10000
010
101
111
3 3
00000
00000
00000
101
101
101

OutPut:
10
7
5
6
5

#include <cstdio>

#include <cstring>

#include <string>

#include <iostream>

#include <queue>

#include <algorithm>

using namespace std;

const int N = 200010;

const int inf = 0x3f3f3f3f;

int dp[1<<15][15];

string s[15];

struct Trie

{

    int ch[N][2],fail[N],last[N],len[N],d[N],pos[15],dis[15][15];

    int root,sz,cnt;

    int newnode()

    {

        for(int i=0;i<2;i++)ch[sz][i]=-1;

        last[sz++]=0;

        return sz-1;

    }

    void init()

    {

        sz=0;cnt=0;

        root=newnode();

    }

    void insert(string str,int id)

    {

        int len=str.size();

        int now=root;

        for(int i=0;i<len;i++)

        {

            int id=str[i]-'0';

            if(ch[now][id]==-1)ch[now][id]=newnode();

            now=ch[now][id];

        }

        last[now]=id;

    }

    void build()

    {

        queue<int>que;

        fail[root]=root;

        for(int i=0;i<2;i++)

        {

            if(ch[root][i]==-1)ch[root][i]=root;

            else

            {

                fail[ch[root][i]]=root;

                que.push(ch[root][i]);

            }

        }

        while(!que.empty())

        {

            int now=que.front();que.pop();

            if(last[fail[now]]==-1)last[now]=-1;

            for(int i=0;i<2;i++)

            {

                if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i];

                else

                {

                    fail[ch[now][i]]=ch[fail[now]][i];

                    que.push(ch[now][i]);

                }

            }

        }

    }

    bool vis[N];

    void bfs(int u,int k)

    {

        memset(vis,false,sizeof(vis));

        memset(d,-1,sizeof(d));

        queue<int>que;

        d[u]=0;vis[u]=true;

        que.push(u);

        while(!que.empty())

        {

            int now=que.front();que.pop();

            for(int i=0;i<2;i++)

            {

                int nxt=ch[now][i];

                if(last[nxt]!=-1&&!vis[nxt])

                {

                    vis[nxt]=true;

                    d[nxt]=d[now]+1;

                    que.push(nxt);

                }

            }

        }

        for(int i=0;i<cnt;i++)dis[k][i]=d[pos[i]];

    }

    int solve(int n)

    {

        for(int i=0;i<(1<<n);i++)

            for(int j=0;j<n;j++)

                dp[i][j]=inf;

        for(int i=0;i<sz;i++)

            if(last[i]>0)pos[cnt]=i,len[cnt++]=s[last[i]].size();

        for(int i=0;i<cnt;i++)bfs(pos[i],i);

        int ans=inf;

        for(int s=0;s<(1<<cnt);s++)

        {

            for(int i=0;i<cnt;i++)

            {

                if(s&(1<<i))

                {

                    if(s==1<<i)dp[s][i]=len[i];

                    for(int j=0;j<cnt;j++)

                    {

                        if(i==j||(s&(1<<j))||dis[i][j]==-1)continue;

                        dp[s|(1<<j)][j]=min(dp[s|(1<<j)][j],dp[s][i]+dis[i][j]);

                    }

                }

            }

        }

        for(int i=0;i<cnt;i++)ans=min(dp[(1<<cnt)-1][i],ans);

        return ans;

    }

}ac;

int n,m;

int cmp(string s1,string s2)

{

    return s1.size()>s2.size();

}

void solve_sub()

{

    int tmp=n,k,vis[15];

    sort(s+1,s+n+1,cmp);

    memset(vis,0,sizeof(vis));

    for(int i=1;i<=n;i++)

        for(int j=i+1;j<=n;j++)

        if(s[i].find(s[j])!=-1)vis[j]=1;

    for(n=0,k=1;k<=tmp;k++)

    {

        if(!vis[k])s[++n]=s[k];

    }

}

int main()

{



    while(scanf("%d%d",&n,&m)>0)

    {

        if(n+m==0)break;

        ac.init();

        for(int i=1;i<=n;i++)

        {

            cin>>s[i];

        }

        solve_sub();

        for(int i=1;i<=n;i++)ac.insert(s[i],i);

        for(int i=0;i<m;i++)

        {

            cin>>s[0];

            ac.insert(s[0],-1);

        }

        ac.build();

        printf("%d\n",ac.solve(n));

    }

}
View Code

 

你可能感兴趣的:(AC自动机)