POJ 2778 AC自动机+矩阵快速幂

来源:互联网 发布:淘宝内部劵骗局 编辑:程序博客网 时间:2024/06/06 04:22

题意

有M个字符串,这M个字符串不能出现在字符串中。要求字符串长度为N,问有多少种方案。

题解

首先,对于这道题,需要知道一个数学定理。对于一张图的邻接矩阵,邻接矩阵的N次幂就是两点距离为N的路径条数。
知道了上述定理以后,这道题就可以看作,字典树从0开始,到某个节点,路径长度为N的方案个数。因为存在不能出现的字符串,结合AC自动机便可解决该题。利用AC自动机对不能出现的字符串,以及后缀子串是不能出现的字符串的节点进行标记。(因为在字典树上,如果想从0开始访问这个节点,那就必须要构成危险字符,才能访问这个节点。)在构建矩阵的时候,被标记的节点值为0。
构建矩阵完成后,利用矩阵快速幂方法进行运算。最后矩阵第一行的和即为所求。

补充说明

事实上,把AC自动机的边映射到矩阵上整个过程详细分析的话还是比较难的。大概可以这样理解。例如拿“ACAGAAA”和”ACAG”进行分析。首先的话,第一个字符我们可以选择A或者GCT,这样的话便有矩阵[3,1,0,0,0,0,0,0]。如果我们要选择第二个字符的话,我们可以看一下,每个节点转移到0状态的路径矩阵[3,2,3,1,0,2,2,0]T。两个矩阵相乘,所代表的意义就是从0出发,经过某个点,再回到0。(从0出发是必要的,但是再回到0不是必要的,因此最后相乘的结果存放在第一行的第一列,第一行其他列代表不回到0的情况)这样依此类推,便可以解决问题。
当然,有一些细节还是要说明一下。比如说ACAGAAA,我们可以看到,转移到0状态的路径矩阵为[3,2,3,1,0,2,2,0]T,也就是说倒数第二个A和倒数第三个A也是有能力转移到0点的。这样的话,会不会存在不符合条件的字符串转移到了0点的情况呢
仔细想一下的话,就会发现这种情况是不存在的。因为如果想要转移到0点,首先就要到达这个点,换句话说就是初始第一行[3,1,0,0,0,0,0,0],经过某些变换后,要能使倒数第二个数和倒数第三个数变为1以上,这样的话才有可能进行转移。
我们可以分析一下,我们可以首先打印一下6号点的转移情况(矩阵第七列),看看谁能转移到6号点,我们发现矩阵为[0,0,0,0,0,1,0,0],在这里我们发现,5号点有一条边可以转移到6号点。(也就是我们已经选择了A,再选一个A)然后,我们再打印一下5号点的矩阵,看看谁能转移到5号点,结果发现是[0,0,0,0,0,0,0,0]。我们发现,4号点和5号点有边相连。原本如果处于四号点状态,再选一个A就可以达到5号点。这时候,对四号点进行处理的作用就显现出来了。对四号点进行过滤,使得四号点不能进行任何状态转移,也不允许任何点转移到四号状态,意义就在于此
我们可以尝试对状态矩阵进行一千次快速幂运算,结果正如我们所想的。0-3号点都不为0,4号点以后都为0。这也就意味着,0-3号点,都可以进行转移,组成字符串。
上述就是我对利用矩阵+AC自动机计算字符串组成方案的一些思考,主要就是为了验证矩阵+AC自动机算法的正确性。如有错误,还望赐教。

代码

#include <iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<string>#include<set>#include<map>#include<bitset>#include<stack>#define UP(i,l,h) for(int i=l;i<h;i++)#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)#define W(a) while(a)#define MEM(a,b) memset(a,b,sizeof(a))#define INF 0x3f3f3f3f3f3f3f3f#define LL long long#define MAXN 10000#define EPS 1e-10#define MOD 100000#define N 2using namespace std;typedef LL MATRIX[110][110];int ch[110][4];char fea[15];int len,sz;int val[110];int f[110];MATRIX a,temp,ans;int getNum(char c) {    if(c=='A') {        return 0;    }    if(c=='C') {        return 1;    }    if(c=='T') {        return 2;    }    if(c=='G') {        return 3;    }}void insert() {    len=strlen(fea);    int u=0;    UP(i,0,len) {        int x=getNum(fea[i]);        if(!ch[u][x]) {            ch[u][x]=sz++;        }        u=ch[u][x];    }//    cout<<u<<endl;    val[u]=1;}void getFail() {    queue<int> q;    MEM(f,0);    UP(x,0,4) {        if(ch[0][x]) {            q.push(ch[0][x]);        }    }    int s=0;    W(!q.empty()) {        int r=q.front();        q.pop();        UP(x,0,4) {            int u=ch[r][x];            if(!u) {                ch[r][x]=ch[f[r]][x];                continue;            }            q.push(u);            int v=f[r];            f[u]=ch[v][x];            if(val[f[u]]==1) {                val[u]=1;            }        }    }}void buildMatrix() {    MEM(a,0);    UP(i,0,sz) {        UP(j,0,4) {            if(val[i]||val[ch[i][j]]) {//                cout<<i<<" "<<j<<" "<<val[i]<<" "<<ch[i][j]<<endl;                continue;            }            a[i][ch[i][j]]++;        }    }}void matrixMulti(MATRIX a,MATRIX b) {    MEM(temp,0);    UP(i,0,sz) {        UP(j,0,sz) {            UP(k,0,sz) {                temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;//                cout<<temp[i][j]<<endl;            }        }    }    memcpy(a,temp,sizeof(temp));}void quick(int n) {    MEM(ans,0);    UP(i,0,sz){            ans[i][i]=1;    }    W(n){        if(n&1){            matrixMulti(ans,a);        }        matrixMulti(a,a);        n>>=1;    }}LL getAns(){    LL sum=0;    UP(i,0,sz){        sum+=ans[0][i];    }    return sum;}int main() {    int m,n;    W(~scanf("%d%d",&m,&n)) {        MEM(val,0);        MEM(ch,0);        sz=1;        UP(i,0,m) {            scanf("%s",fea);            insert();        }        getFail();        buildMatrix();        quick(n);        printf("%I64d\n",getAns()%MOD);    }}/*2 20000000000ACGC*/