POJ 2778 DNA Sequence(AC自动机 + 矩阵快速幂)题解
题意:给出m个模式串,要求你构造长度为n(n <= 2000000000)的主串,主串不包含模式串,问这样的主串有几个
思路:因为要不包含模式串,显然又是ac自动机。因为n很大,所以用dp不太好。
在图论中,如果我们知道一个图的邻接矩阵A,$A_{ij}$ = 1表示i走一步到j有一条路,那么$A^n$中的$A_{ij}$就是这个图中从i走n步到j的路径数。
所以用ac自动机我们创造一个所有后缀的邻接矩阵A,那么用矩阵快速幂$A^n$就求出了所有的路径数,$\sum_{i = 1}^n A_{0i}$就是从root走到所有可行后缀的所有走法。
代码:
#include<cmath> #include<set> #include<map> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include <iostream> #include<algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn = 100 + 5; const int M = 50 + 5; const ull seed = 131; const double INF = 1e20; const int MOD = 100000; int m, tn; ll n; struct Mat{ ll s[maxn][maxn]; }; Mat mul(Mat &a, Mat &b){ Mat t; memset(t.s, 0, sizeof(t.s)); for(int i = 0; i < tn; i++){ for(int j = 0; j < tn; j++){ for(int k = 0; k < tn; k++){ t.s[i][j] = (t.s[i][j] + a.s[i][k] * b.s[k][j])%MOD; } } } return t; } Mat ppow(Mat a, ll b){ Mat ret; memset(ret.s, 0, sizeof(ret.s)); for(int i = 0; i < maxn; i++) ret.s[i][i] = 1; while(b){ if(b & 1) ret = mul(ret, a); a = mul(a, a); b >>= 1; } return ret; } int id(char a){ if(a == 'A') return 0; if(a == 'T') return 1; if(a == 'C') return 2; if(a == 'G') return 3; } struct Aho{ struct state{ int next[4]; int fail, cnt; }node[maxn]; int size; queue<int> q; void init(){ size = 0; newtrie(); while(!q.empty()) q.pop(); } int newtrie(){ memset(node[size].next, 0, sizeof(node[size].next)); node[size].cnt = node[size].fail = 0; return size++; } void insert(char *s){ int len = strlen(s); int now = 0; for(int i = 0; i < len; i++){ int c = id(s[i]); if(node[now].next[c] == 0){ node[now].next[c] = newtrie(); } now = node[now].next[c]; } node[now].cnt = 1; } void build(){ node[0].fail = -1; q.push(0); while(!q.empty()){ int u = q.front(); q.pop(); if(node[node[u].fail].cnt && u) node[u].cnt = 1; //都不能取 for(int i = 0; i < 4; i++){ if(!node[u].next[i]){ if(u == 0) node[u].next[i] = 0; else node[u].next[i] = node[node[u].fail].next[i]; } else{ if(u == 0) node[node[u].next[i]].fail = 0; else{ int v = node[u].fail; while(v != -1){ if(node[v].next[i]){ node[node[u].next[i]].fail = node[v].next[i]; break; } v = node[v].fail; } if(v == -1) node[node[u].next[i]].fail = 0; } q.push(node[u].next[i]); } } } } void query(){ Mat a; memset(a.s, 0, sizeof(a.s)); for(int i = 0; i < size; i++){ for(int j = 0; j < 4; j++){ if(node[node[i].next[j]].cnt == 0){ a.s[i][node[i].next[j]]++; } } } a = ppow(a, n); ll ans = 0; for(int i = 0; i < size; i++){ if(node[i].cnt == 0) ans = (ans + a.s[0][i]) % MOD; } printf("%lld\n", ans); } }ac; char s[20]; int main(){ while(~scanf("%d%lld", &m, &n)){ ac.init(); while(m--){ scanf("%s", s); ac.insert(s); } ac.build(); tn = ac.size; ac.query(); } return 0; }