poj--2778DNA Sequence+AC自动机+矩阵快速幂

题目链接:点击进入
如果我们先将所有的病毒字符串构建成一棵trie,然后构建一个n长的字符串的过程可以看成是从这颗trie树根节点出发走n步的一个过程,为了不含任何的病毒,则在走的过程中不能经过任何的病毒节点。考虑只走一步的过程,则我们可以得到一个矩阵m[i][j],表示从节点i到节点j有多少种方式。那么这个矩阵的n次幂就是表示走n步的情况,然后也就可以得到答案了。
问题在于如何获得这个走一步方案数的矩阵,我们可以利用构造ac自动机的过程,将所有的病毒叶子节点以及以这些节点做后缀的节点标记为病毒节点,然后就可以通过检查每个节点的所有儿子节点得到到达他们的方案数了。更具体的思路见代码

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

#define ll long long

struct Matrix
{
    ll mat[140][140];
    int n;
    Matrix(){};
    Matrix(int n1)
    {
        n=n1;
        for(int i=0;i<n;i++)
          for(int j=0;j<n;j++)
             mat[i][j]=0;
    }
    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];
            ret.mat[i][j]%=100000;
          }
        return ret;
    }
};

Matrix matrixPow(Matrix matrix,int n)
{
    Matrix ret = Matrix(matrix.n);
    for(int i=0;i<matrix.n;i++)
      ret.mat[i][i]=1;
    while(n)
    {
        if(n&1) ret=ret*matrix;
        matrix=matrix*matrix;
        n=n>>1;
    }
    return ret;
}

int getIndex(char x)
{
   if(x=='A') return 0;
   if(x=='T') return 1;
   if(x=='C') return 2;
   if(x=='G') return 3;
}

struct Trie
{
    int next[140][4],fail[140],flag[140];
    int root,L;
    int newnode()
    {
        memset(next[L],-1,sizeof(next[L]));
        flag[L++]=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++)
        {
            int index=getIndex(buf[i]);
            if(next[now][index]==-1)
               next[now][index]=newnode();
            now=next[now][index];
        }
        flag[now]++;
    }
    void build()
    {
        queue<int>Q;
        fail[root]=root;
        for(int i=0;i<4;i++)
        {
            if(next[root][i]==-1)
               next[root][i]=root;
            else
            {
                fail[next[root][i]]=root;
                Q.push(next[root][i]);
            }
        }
        while(!Q.empty())
        {
            int now=Q.front();
            Q.pop();
            ///如果当前字符串的后缀节点是病毒,则当前字符串也是病毒
            ///这是因为更新的时候会先更新浅层的然后再更新深层的
            ///所以要用浅层的标记去更新深层的
            if(flag[fail[now]])
              flag[now]++;
            for(int i=0;i<4;i++)
            {
                if(next[now][i]==-1)
                  next[now][i]=next[fail[now]][i];
                else
                {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
            }
        }
    }
    ///获得对应的状态转移矩阵
    ///对trie中的每个节点都检查其所有儿子节点
    void getMatrix(Matrix &matrix)
    {
        for(int i=0;i<L;i++)
          for(int j=0;j<4;j++)
             if(!flag[i]&&!flag[next[i][j]])
                matrix.mat[i][next[i][j]]++;
    }
};

Trie ac;
char str[20];

int main()
{
    int m,n;
    //freopen("in.txt","r",stdin);
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        Matrix matrix = Matrix(50);
        ac.init();
        for(int i=0;i<m;i++)
        {
            scanf("%s",str);
            ac.insert(str);
        }
        ac.build();
        ac.getMatrix(matrix);
        matrix=matrixPow(matrix,n);
        ll ans=0;
        //check(matrix);
        for(int i=0;i<ac.L;i++)
          ans+=matrix.mat[0][i];
        printf("%lld\n",ans%100000);
    }
  return 0;
}

你可能感兴趣的:(矩阵快速幂,AC自动机)