题目链接: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
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原创,但可以转载,因为我们是兄弟。