题目链接: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;
}