POJ 2778 DNA Sequence (AC自动机+矩阵快速幂)

题目链接:http://poj.org/problem?id=2778


题目大意:给出m个病毒串。构造一个长度为n的串(由A、G、C、T组成),使其不包含任何一个病毒串,求方案数。


思路:将病毒串构造AC自动机。接下来我们可以定义函数f(u, n)表示从自动机上的结点u出发,还需走n步的方案数,那么有f(u, n) = sigma( f(ch[u][v], m-1) ),其中u->v为自动机上的边,并且v不能是单词节点),答案即为f(0, n)。但是m太大,直接计算是不可能的。转化一下,我们的状态转移都是在自动机上的结点进行,我们再定义g(u, x, v)表示从u出发走x步到达v的方案数。因此,f(0, n)等价于sigma( g(0, v, n) ,v是自动机上的任意结点 )。而这个g函数可以用离散数学中的知识求出来。首先,将自动机看成一个有向图,求出其邻接矩阵A(A[i][j]表示从点i到点j的边的条数,也可以表示从点i走1步到点j的方案数,,注意重边),计算B=A^n,那么B[i][j]即表示从点i走n步到点j的方案数。


#include<cstdio>
#include<cstring>
#include<string>
#include<cctype>
#include<iostream>
#include<set>
#include<map>
#include<cmath>
#include<sstream>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#define fin freopen("a.txt","r",stdin)
#define fout freopen("a.txt","w",stdout)
typedef long long LL;
using namespace std;
typedef pair<int, int> P;
const int INF = 1e8 + 10;
const int maxn = 100 + 10;
const int maxnode = 100 + 10;
const int mod = 100000;
const int sigma_size = 4;
char in[maxn];
int M;
LL N;

struct Matrix
{
    int r, c;
    LL a[maxn][maxn];
    void init(int r, int c, int x) {
       this->r = r;
       this->c = c;
       for(int i = 1; i <= r; i++)
        for(int j = 1; j <= c; j++)
           a[i][j] = x;
    }

    void out() {
       for(int i = 1; i <= r; i++)
        for(int j = 1; j <= c; j++)
           printf("%lld%c", a[i][j], j == c ? '\n' : ' ');
    }
};

Matrix E, A;

Matrix operator + (const Matrix &A, const Matrix &B)
{
    int r = A.r, c = A.c;
    Matrix C; C.init(r, c, 0);
    for(int i = 1; i <= r; i++)
      for(int j = 1; j <= c; j++)
         C.a[i][j] = LL(A.a[i][j] + B.a[i][j]) % mod;
    return C;
}

Matrix operator * (const Matrix &A, const Matrix &B)
{
    int r = A.r, c = B.c;
    Matrix C; C.init(r, c, 0);
    for(int i = 1; i <= r; i++)
      for(int j = 1; j <= c; j++)
        for(int k = 1; k <= B.r; k++)
           C.a[i][j] = LL(A.a[i][k] * B.a[k][j] + C.a[i][j]) % mod;
    return C;
}

Matrix Pow(Matrix A, LL p)
{
    Matrix res = E;
    while(p)
    {
        if(p & 1) res = res * A;
        p >>= 1;
        A = A * A;
    }
    return res;
}

struct AC
{
	int ch[maxnode][sigma_size];
	int f[maxnode];
	int vis[maxnode];
    bool flag[maxnode];
	int sz;
	void init() { sz = 1; memset(ch[0], 0, sizeof ch[0]); memset(vis, 0, sizeof vis);}
	int idx(char c) { if(c == 'A') return 0; if(c == 'G') return 1; if(c == 'C') return 2; if(c == 'T') return 3; }

	void insert(char *s)
	{
		int u = 0, n = strlen(s);
		for(int i = 0; i < n; i++)
		{
			int c = idx(s[i]);
			if(!ch[u][c])
			{
				memset(ch[sz], 0, sizeof ch[sz]);
				flag[sz] = 0;
				ch[u][c] = sz++;
			}
			u = ch[u][c];
		}
		flag[u] = true;
	}

	Matrix getMatrix()
	{
	    Matrix A; A.init(sz, sz, 0);
	    for(int i = 0; i < sz; i++)
           for(int j = 0; j < sigma_size; j++)
              if(!flag[ch[i][j]])
                 ++A.a[i+1][ch[i][j]+1];
	    return A;
	}

    int getFail()
    {
    	queue<int> q;
    	f[0] = 0;
    	for(int c = 0; c < sigma_size; c++)
    	{
    		int u = ch[0][c];
    		if(u) { f[u] = 0; q.push(u);}
    	}

    	while(!q.empty())
    	{
            int r = q.front(); q.pop();
            for(int c = 0; c < sigma_size; c++)
            {
            	int u = ch[r][c];
            	if(!u) { ch[r][c] = ch[f[r]][c]; continue; }
            	q.push(u);
            	int v = f[r];
            	while(v && !ch[v][c]) v = f[v];
            	f[u] = ch[v][c];
            	flag[u] |= flag[f[u]];
            }
    	}
    }

}ac;

int main()
{
	cin >> M >> N;
	ac.init();
	for(int i = 1; i <= M; i++)
	{
        scanf("%s", in);
        ac.insert(in);
	}
	ac.getFail();
	E.init(ac.sz, ac.sz, 0);
	for(int i = 1; i <= E.r; i++) E.a[i][i] = 1;
	A = Pow(ac.getMatrix(), N);
	LL ans = 0;
	for(int i = 1; i <= ac.sz; i++)
        ans = (ans + A.a[1][i]) % mod;
    cout << ans << endl;
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值