【POJ 2778】DNA Sequence
阿新 • • 發佈:2020-09-07
題目
題目連結:http://poj.org/problem?id=2778
給出 \(n\) 個長度不超過 \(10\) 且僅由 \(\operatorname{A,C,T,G}\) 組成的串 \(s_i\),問有多少個長度為 \(m\) 的僅由 \(\operatorname{A,C,T,G}\) 串不存在任意一個子串為 \(s\)。
\(n\leq 10,m\leq 2\times 10^9\)。
吐槽
從星期五開始寫,差不多 \(40min\) 過了樣例,但是交到 POJ 上一直 WA。
週六發現是矩陣乘法中兩個元素相乘可能超過 int 的範圍,改了之後一直 TLE。
週日早上心態大崩開始和標程對拍,發現完全沒有問題。一度懷疑是 POJ 評測環境的問題。但是標程本地需要 \(70ms\)
晚上優化了矩陣乘法的常數,程式碼降到了 \(70ms\),終於在 POJ 上過了。跑了 \(500+ms\)。。。
POJnb。
思路
首先建立 AC 自動機,定義一個點 \(x\) 有 \(end[x]=1\) 當且僅當滿足以下條件之一:
- 存在一個串在點 \(x\) 結束。
- 存在一個串在點 \(x\) 的祖先結束
- \(end[fail[x]]=1\)。
那麼如果 \(end[x]=1\),那麼最終的串就不能存在一個子串等於根到 \(x\) 所構成的字串。
那麼可以把加字母的過程看做在 AC 自動機上一個點在移動,假設要加入一個字母 \(a\)
那麼一個串是合法的當且僅當這一個點沒有經過 \(end=1\) 的點。
由於 AC 自動機上的點不超過 \(n\times |S|\leq 100\) 個,可以用矩陣乘法優化。
時間複雜度 \(O(n^3|S|^3\log m)\)。
程式碼
#include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N=110,M=5,MOD=100000; int n,m,Q,ans; char s[N]; inline int ID(char ch) { if (ch=='A') return 1; if (ch=='C') return 2; if (ch=='T') return 3; return 4; } struct Matrix { int a[N][N]; Matrix() { memset(a,0,sizeof(a)); } }mat; struct ACA { int tot,c[N][M],fail[N]; bool end[N]; inline void ins(char *s) { int len=strlen(s),p=0; for (int i=0;i<len;i++) { int id=ID(s[i]); if (!c[p][id]) c[p][id]=++tot; p=c[p][id]; } end[p]=1; } inline void build() { queue<int> q; for (int i=1;i<=4;i++) if (c[0][i]) q.push(c[0][i]); while (q.size()) { int u=q.front(); q.pop(); if (end[fail[u]]) end[u]=1; for (int i=1;i<=4;i++) if (c[u][i]) { fail[c[u][i]]=c[fail[u]][i]; q.push(c[u][i]); if (end[u]) end[c[u][i]]=1; } else c[u][i]=c[fail[u]][i]; } } inline void updmat() { for (int i=0;i<=tot;i++) for (int j=1;j<=4;j++) if (!end[i] && !end[c[i][j]]) mat.a[i][c[i][j]]++; } }AC; Matrix operator *(Matrix a,Matrix b) { Matrix c; for (int i=0;i<=AC.tot;i++) for (int j=0;j<=AC.tot;j++) for (int k=0;k<=AC.tot;k++) //這個地方 AC.tot 寫成 N 就會 T。。。 c.a[i][j]=(c.a[i][j]+1LL*a.a[i][k]*b.a[k][j])%MOD; return c; } inline Matrix fpow(Matrix a,int k) { Matrix b; b.a[0][0]=1; for (;k;k>>=1,a=a*a) if (k&1) b=b*a; return b; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { scanf("%s",s); AC.ins(s); } AC.build(); AC.updmat(); mat=fpow(mat,m); for (int i=0;i<=AC.tot;i++) ans=(ans+mat.a[0][i])%MOD; printf("%d\n",ans); return 0; }