题目链接:http://poj.org/problem?id=2778
题目大意:给定‘A''C''G''T'四个碱基组成的m个病毒字符串,求碱基组成的长度为n的不包含病毒串的字符串种数,结果模以10万。
解题思路:状态转移方程很容易想:if (j->next非危险节点) dp[i+1][j->next] += dp[i][j];(i表示长度,j->next为自动机上j位置下一个到达的位置)。但这题的n也就是长度异常地大,用普通的dp做太不靠谱了。刚学完矩阵乘法(开始时觉得很难,但看了一晚上发现就会做了),想着能否用矩阵来求解。由于上述方程并非线性的,就算转为线性也非常麻烦,再想着构造其他模型。
其实这题里的AC自动机也是一张有向图,i->j有边当且仅当i-》next == j.所以转移也就是图上的某个点走到另一个点,那遇到病毒串是什么情况?肯定没办法到病毒串末尾的危险节点,所以没有边连到危险节点。这样的话模型转换为求有向图上0节点到其他能到达的节点的路径,并且经过n条边。离散数学里面有说把这个矩阵A进行A^n计算然后累加就ok。
本题的取模数为10万,在乘法运算中容易超32位int,所以矩阵中的数组应为64位整数。
测试数据:
0 3
AT
AC
AG
AA
AT
AC
AG
AA
代码:
#include <stdio.h>
#include <string.h>
#define MAX 110
#define MOD 100000
char dir[MAX];
int map[MAX][MAX];
int n,m,total,hash[256];
struct node {
int flag,in;
node *fail,*next[4];
}*qu[MAX*5],arr[MAX],*root;
struct Mat {
__int64 mat[MAX][MAX],size;
Mat(int n) {size = n,memset(mat,0,sizeof(mat));}
friend Mat operator *(Mat a,Mat b);
friend Mat operator +(Mat a,Mat b);
friend Mat operator ^(Mat a,Mat b);
}E(MAX),A(MAX);
Mat operator *(Mat a,Mat b) {
Mat c(total);
for (int i = 0; i < c.size; ++i)
for (int j = 0; j < c.size; ++j)
for (int k = 0; k < c.size; ++k)
if (a.mat[i][k] && b.mat[k][j])
c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % MOD;
return c;
}
Mat operator +(Mat a,Mat b) {
Mat c(total);
for (int i = 0; i < c.size; ++i)
for (int j = 0; j < c.size; ++j)
c.mat[i][j] = (a.mat[i][j] + b.mat[i][j]) % MOD;
return c;
}
Mat operator ^(Mat a,int k) {
Mat c = E;
while (k) {
if (k & 1) c = c * a;
a = a * a,k >>= 1;
}
return c;
}
node *CreateNode() {
node *p = &arr[total];
p->in = total++;
p->flag = 0;
p->fail = NULL;
for (int i = 0; i < 4; ++i)
p->next[i] = NULL;
return p;
}
void Insert(char *str) {
int i = 0,k;
node *p = root;
while (str[i]) {
k = hash[str[i++]];
if (p->next[k] == NULL)
p->next[k] = CreateNode();
p = p->next[k];
}
p->flag = 1;
}
void Build_AC() {
int i,k,head,tail;
head = tail = 0;
root->fail = root;
qu[++head] = root;
while (tail < head) {
node *p = qu[++tail];
for (k = 0; k < 4; ++k)
if (p->next[k] != NULL) {
if (p == root) p->next[k]->fail = root;
else p->next[k]->fail = p->fail->next[k];
if (p->fail->next[k]->flag == 1)
p->next[k]->flag = 1;
qu[++head] = p->next[k];
}
else {
if (p == root) p->next[k] = root;
else p->next[k] = p->fail->next[k];
}
}
}
void Create_Mat() {
int i,j,k;
A.size = E.size = total;
for (i = 0; i < E.size; ++i)
E.mat[i][i] = 1;
for (i = 0; i < total; ++i)
if (arr[i].flag == 0) {
for (k = 0; k < 4; ++k)
if (arr[i].next[k]->flag == 0)
A.mat[i][arr[i].next[k]->in]++;
}
}
int main()
{
int i,j,k,ans;
hash['A'] = 0,hash['C'] = 1;
hash['G'] = 2,hash['T'] = 3;
while (scanf("%d%d",&m,&n)!= EOF) {
total = ans = 0;
root = CreateNode();
memset(A.mat,0,sizeof(A.mat));
for (i = 0; i < m; ++i)
scanf("%s",dir),Insert(dir);
Build_AC();
Create_Mat();
A = A^n;
for (i = 0; i < total; ++i)
if (arr[i].flag == 0)
ans = (ans + A.mat[0][arr[i].in]) % MOD;
printf("%d\n",ans);
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。