码迷,mamicode.com
首页 > 其他好文 > 详细

AC自动机小结

时间:2015-04-23 23:08:44      阅读:283      评论:0      收藏:0      [点我收藏+]

标签:

  

    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自动机小结

标签:

原文地址:http://www.cnblogs.com/lienus/p/4451966.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!