poj2778 & hdu 2243 AC自动机+矩阵 &&BZOJ2938

来源:互联网 发布:易语言和java 编辑:程序博客网 时间:2024/06/08 06:32

AC自动机。
具体可以详看 [AC自动机](http://www.cppblog.com/menjitianya/archive/2014/07/10/207604.html)

做这几道题需要注意一点、、如果某节点A的fail指针指向标志节点,那么这个节点A也是标志节点。如图
这里写图片描述
节点4和节点6肯定是病毒,由于节点3的fail是节点6且是病毒,导致3也是病毒。病毒的递推关系可以传递。

对DNA建立好AC自动机。如果需要找出一条DNA链不包含病毒DNA,那么也就是说这条DNA链一直无法在AC自动机上面进行匹配。也就是不经过标志节点。怎么找出这些DNA链?只要在不是标志的点来回转动就可以了。
那如何找出恰好走了N步的DNA链的数量?
建立好的AC自动机就是一个有向图。那么

x:
把给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j。 令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就等于从点i到点j恰好经过2条边的路径数(枚举k为中转点)。类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数。同理,如果要求经过k步的路径数,我们只需要二分求出A^k即可。

所以总的来说方法就是:
对模式串建立AC自动机。对建成的AC自动机(有向图)生成矩阵。然后求矩阵快速次幂。。

poj2778代码:

#include <algorithm>#include <algorithm>#include <iostream>#include<string.h>#include <fstream>#include <math.h>#include <vector>#include <cstdio>#include <string>#include <queue>#include <stack>#include <map>#include <set>#define exp 1e-8#define fi first#define se second#define ll long long#define INF 0x3f3f3f3f#define lson l,mid,rt<<1#define pb(a) push_back(a)#define mp(a,b) make_pair(a,b)#define rson mid+1,r,(rt<<1)+1#define all(a) a.begin(),a.end()#define mm(a,b) memset(a,b,sizeof(a));#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)#define rep(a,b,c) for(int a=b;a<=c;a++)//b---c#define repp(a,b,c)for(int a=b;a>=c;a--)///using namespace std;void bug(string m="here"){cout<<m<<endl;}template<typename __ll> inline void READ(__ll &m){__ll x=0,f=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}template<typename __ll>inline void read(__ll &m){READ(m);}template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c){READ(m);READ(a);READ(b);READ(c);}template < class T > T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template < class T > T lcm(T a, T b) { return a / gcd(a, b) * b; }template < class T > inline void rmin(T &a, const T &b) { if(a > b) a = b; }template < class T > inline void rmax(T &a, const T &b) { if(a < b) a = b; }template < class T > T pow(T a, T b) { T r = 1; while(b > 0) { if(b & 1) r = r * a; a = a * a; b /= 2; } return r; }template < class T > T pow(T a, T b, T mod) { T r = 1; while(b > 0) { if(b & 1) r = r * a % mod; a = a * a % mod; b /= 2; } return r; }const int kk=110;struct matrix{    int mat[kk][kk];    int n;    matrix(){}    matrix(int _n)    {        n=_n;        memset(mat,0,sizeof(mat));    }    matrix operator *(const matrix &b)const    {        matrix ret=matrix(n);        for(int i=0;i<n;i++)            for(int j=0;j<n;j++)            {                for(int k=0;k<n;k++)                    ret.mat[i][j]+=(long long)mat[i][k]*b.mat[k][j]%100000;                ret.mat[i][j]%=100000;            }        return ret;    }};matrix q_pow(matrix a,int b){    matrix ret=matrix(a.n);    for(int i=0;i<a.n;i++)        ret.mat[i][i]=1;    while(b)    {        if(b&1)ret=ret*a;        a=a*a;        b>>=1;    }    return ret;}const int maxn=100+10;  //字典树可能有的节点数量const int num_son=4;int id(char ch){    if(ch=='A')return 0;    if(ch=='T')return 1;    if(ch=='C')return 2;    if(ch=='G')return 3;}struct Trie{    int next[maxn][num_son],fail[maxn];    bool end[maxn];//分别是字典树、失败指针、是否为完整串标志    int root,l;    int newnode()    {        for(int i=0;i<num_son;i++)            next[l][i]=-1;        end[l++]=0;  //此处  当end[i]>0  意味这个从根到这个点是一个完整的串        return l-1;    }    void init()    {        l=0;        root=newnode();    }    void insert(char buf[])    {        int len=strlen(buf);        int now=root;        for(int i=0;i<len;i++)        {            if(next[now][id(buf[i])]==-1)                next[now][id(buf[i])]=newnode();            now=next[now][id(buf[i])];        }        end[now]=1;    }    void build()  //build了之后 之前的next[][]==-1的 统统转到失败节点的下一个~~~    {        queue<int>que;        fail[root]=root;        for(int i=0;i<num_son;i++)            if(next[root][i]==-1)                next[root][i]=root;            else            {                fail[next[root][i]]=root;                que.push(next[root][i]);            }        while(!que.empty())        {            int now=que.front();            que.pop();            if(end[fail[now]]==1)                end[now]=1;  //传递病毒关系。都是从fail节点(已经访问过)传递。反正就是从上(已经访问过的fail)往下(当前节点)传递。具有传递关系            for(int i=0;i<num_son;i++)                if(next[now][i]==-1)                    next[now][i]=next[fail[now]][i];                else                {                    fail[next[now][i]]=next[fail[now]][i];                    que.push(next[now][i]);                }        }    }    matrix build_matrix()    {        matrix ret=matrix(l);        for(int i=0;i<l;i++)            for(int j=0;j<num_son;j++)            {                if(!end[i]&&!end[next[i][j]])                    ret.mat[i][next[i][j]]++;            }        return ret;    }    void debug()    {        for(int i = 0;i <l;i++)        {            printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);            for(int j = 0;j < num_son;j++)                printf("%2d",next[i][j]);            printf("]\n");        }    }}ac;char buf[1000000+10];int main(){    int n,m;    while(scanf("%d %d",&n,&m)!=EOF)    {        ac.init();        for(int i=0;i<n;i++)        {            scanf("%s",buf);            ac.insert(buf);        }        ac.build();ac.debug();        matrix ac_matrix=ac.build_matrix();        matrix ans=q_pow(ac_matrix,m);        int cnt=0;        for(int i=0;i<ac.l;i++)            cnt+=ans.mat[0][i],cnt%=100000;        printf("%d\n",cnt);        cout<<ac.l<<endl;    }    return 0;}

hdu2243:
包含,那就是减法,求出全部的然后减去不包含的。。poj2778就是不包含的。
建立AC自动机,构造矩阵A….
这里困难的地方就是要求《=m长度的总和~~~
不包含的数量就是A^1的第一行sum+A^1的第一行sum+…….A^m的第一行sum……这个直接求有点困难。
对矩阵A新增一行一列。列全为1,行设为0。求出(A^m的第一行sum-1)就是 上诉式子和。

#include <algorithm>#include <algorithm>#include <iostream>#include<string.h>#include <fstream>#include <math.h>#include <vector>#include <cstdio>#include <string>#include <queue>#include <stack>#include <map>#include <set>#define exp 1e-8#define fi first#define se second#define ll long long#define INF 0x3f3f3f3f#define lson l,mid,rt<<1#define pb(a) push_back(a)#define mp(a,b) make_pair(a,b)#define rson mid+1,r,(rt<<1)+1#define all(a) a.begin(),a.end()#define mm(a,b) memset(a,b,sizeof(a));#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)#define rep(a,b,c) for(int a=b;a<=c;a++)//b---c#define repp(a,b,c)for(int a=b;a>=c;a--)///using namespace std;void bug(string m="here"){cout<<m<<endl;}template<typename __ll> inline void READ(__ll &m){__ll x=0,f=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}template<typename __ll>inline void read(__ll &m){READ(m);}template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c){READ(m);READ(a);READ(b);READ(c);}template < class T > T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template < class T > T lcm(T a, T b) { return a / gcd(a, b) * b; }template < class T > inline void rmin(T &a, const T &b) { if(a > b) a = b; }template < class T > inline void rmax(T &a, const T &b) { if(a < b) a = b; }template < class T > T pow(T a, T b) { T r = 1; while(b > 0) { if(b & 1) r = r * a; a = a * a; b /= 2; } return r; }template < class T > T pow(T a, T b, T mod) { T r = 1; while(b > 0) { if(b & 1) r = r * a % mod; a = a * a % mod; b /= 2; } return r; }const int kk=50;struct matrix{    unsigned long long mat[kk][kk];    int n;    matrix(){}    matrix(int _n)    {        n=_n;        memset(mat,0,sizeof(mat));    }    matrix operator *(const matrix &b)const    {        matrix ret=matrix(n);        for(int i=0;i<n;i++)            for(int j=0;j<n;j++)                for(int k=0;k<n;k++)                    ret.mat[i][j]+=mat[i][k]*b.mat[k][j];        return ret;    }};matrix q_pow(matrix a,int b){    matrix ret=matrix(a.n);    for(int i=0;i<a.n;i++)        ret.mat[i][i]=1;    while(b)    {        if(b&1)ret=ret*a;        a=a*a;        b>>=1;    }    return ret;}const int maxn=50+10;  //字典树可能有的节点数量const int num_son=26;struct Trie{    int next[maxn][num_son],fail[maxn];    bool end[maxn];//分别是字典树、失败指针、是否为完整串标志    int root,l;    int newnode()    {        for(int i=0;i<num_son;i++)            next[l][i]=-1;        end[l++]=0;  //此处  当end[i]>0  意味这个从根到这个点是一个完整的串        return l-1;    }    void init()    {        l=0;        root=newnode();    }    void insert(char buf[])    {        int len=strlen(buf);        int now=root;        for(int i=0;i<len;i++)        {            if(next[now][buf[i]-'a']==-1)                next[now][buf[i]-'a']=newnode();            now=next[now][buf[i]-'a'];        }        end[now]=1;    }    void build()  //build了之后 之前的next[][]==-1的 统统转到失败节点的下一个~~~    {        queue<int>que;        fail[root]=root;        for(int i=0;i<num_son;i++)            if(next[root][i]==-1)                next[root][i]=root;            else            {                fail[next[root][i]]=root;                que.push(next[root][i]);            }        while(!que.empty())        {            int now=que.front();            que.pop();            if(end[fail[now]]==1)                end[now]=1;            for(int i=0;i<num_son;i++)                if(next[now][i]==-1)                    next[now][i]=next[fail[now]][i];                else                {                    fail[next[now][i]]=next[fail[now]][i];                    que.push(next[now][i]);                }        }    }    matrix build_matrix()    {        matrix ret=matrix(l+1);        for(int i=0;i<l;i++)            for(int j=0;j<num_son;j++)            {                if(!end[i]&&!end[next[i][j]])                    ret.mat[i][next[i][j]]++;            }        for(int i=0;i<l+1;i++) //为了求和            ret.mat[i][l]=1;        return ret;    }    void debug()    {        for(int i = 0;i < l;i++)        {            printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);            for(int j = 0;j < num_son;j++)                printf("%2d",next[i][j]);            printf("]\n");        }    }}ac;char buf[50];int main(){    int n,m;    while(scanf("%d %d",&n,&m)!=EOF)    {        ac.init();        for(int i=0;i<n;i++)        {            scanf("%s",buf);            ac.insert(buf);        }        ac.build();        matrix ac_matrix=ac.build_matrix();        matrix tmp=q_pow(ac_matrix,m);        unsigned long long ret=0;        for(int i=0;i<tmp.n;i++)            ret+=tmp.mat[0][i];        ret--;//ret包含从0次幂到m次幂的和,,,去掉0次幂的        matrix a=matrix(2);        a.mat[0][0]=26;        a.mat[1][0]=a.mat[1][1]=1;        a=q_pow(a,m);        unsigned long long ans=a.mat[1][0]+a.mat[0][0];        ans--;        cout<<ans-ret<<endl;    }    return 0;}

BZOJ 2938:
问是否有一个无限长的01序列不包含题目给出的病毒01序列。
其实和poj2778一样。
无限长的01序列无法在AC自动机上面进行匹配。
也就是说这个01序列一直在非标志节点进行循环~~~
只要在这些非标志节点里存在一个环即可。DSF

#include <algorithm>#include <algorithm>#include <iostream>#include<string.h>#include <fstream>#include <math.h>#include <vector>#include <cstdio>#include <string>#include <queue>#include <stack>#include <map>#include <set>#define exp 1e-8#define fi first#define se second#define ll long long#define INF 0x3f3f3f3f#define lson l,mid,rt<<1#define pb(a) push_back(a)#define mp(a,b) make_pair(a,b)#define rson mid+1,r,(rt<<1)+1#define all(a) a.begin(),a.end()#define mm(a,b) memset(a,b,sizeof(a));#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)#define rep(a,b,c) for(int a=b;a<=c;a++)//b---c#define repp(a,b,c)for(int a=b;a>=c;a--)///using namespace std;void bug(string m="here"){cout<<m<<endl;}template<typename __ll> inline void READ(__ll &m){__ll x=0,f=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}template<typename __ll>inline void read(__ll &m){READ(m);}template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c){READ(m);READ(a);READ(b);READ(c);}template < class T > T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template < class T > T lcm(T a, T b) { return a / gcd(a, b) * b; }template < class T > inline void rmin(T &a, const T &b) { if(a > b) a = b; }template < class T > inline void rmax(T &a, const T &b) { if(a < b) a = b; }template < class T > T pow(T a, T b) { T r = 1; while(b > 0) { if(b & 1) r = r * a; a = a * a; b /= 2; } return r; }template < class T > T pow(T a, T b, T mod) { T r = 1; while(b > 0) { if(b & 1) r = r * a % mod; a = a * a % mod; b /= 2; } return r; }const int maxn=30000+10;  //字典树可能有的节点数量const int num_son=2;struct Trie{    int next[maxn][num_son],fail[maxn];    bool end[maxn];//分别是字典树、失败指针、是否为完整串标志    int root,l;    int vis[maxn];    int newnode()    {        for(int i=0;i<num_son;i++)            next[l][i]=-1;        end[l++]=0;  //此处  当end[i]>0  意味这个从根到这个点是一个完整的串        return l-1;    }    void init()    {        l=0;        memset(vis,0,sizeof vis);        root=newnode();    }    void insert(char buf[])    {        int len=strlen(buf);        int now=root;        for(int i=0;i<len;i++)        {            if(next[now][buf[i]-'0']==-1)                next[now][(buf[i]-'0')]=newnode();            now=next[now][(buf[i]-'0')];        }        end[now]=1;    }    void build()  //build了之后 之前的next[][]==-1的 统统转到失败节点的下一个~~~    {        queue<int>que;        fail[root]=root;        for(int i=0;i<num_son;i++)            if(next[root][i]==-1)                next[root][i]=root;            else            {                fail[next[root][i]]=root;                que.push(next[root][i]);            }        while(!que.empty())        {            int now=que.front();            que.pop();            if(end[fail[now]]==1)                end[now]=1;            for(int i=0;i<num_son;i++)                if(next[now][i]==-1)                    next[now][i]=next[fail[now]][i];                else                {                    fail[next[now][i]]=next[fail[now]][i];                    que.push(next[now][i]);                }        }    }    void debug()    {        for(int i = 0;i < l;i++)        {            printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);            for(int j = 0;j < num_son;j++)                printf("%2d",next[i][j]);            printf("]\n");        }    }    bool dfs(int idx) //当前节点、    {        if(vis[idx]==2)return 0;        for(int i=0;i<num_son;i++)            if(vis[next[idx][i]]==1)return 1;            else if(vis[next[idx][i]]==0&&!end[next[idx][i]])            {                vis[next[idx][i]]=1;                if(dfs(next[idx][i]))                    return 1;            }        vis[idx]=2;        return 0;    }}ac;char buf[30000+10];int main(){    int n;read(n);    ac.init();    for(int i=0;i<n;i++)    {        scanf("%s",buf);        ac.insert(buf);    }    ac.build();    //ac.debug();    if(ac.dfs(ac.root))printf("TAK\n");    else printf("NIE");    return 0;}
0 0