It’s well known that DNA Sequence is a sequence only contains A, C, T and G, and it’s very useful to analyze a segment of DNA Sequence,For example, if a animal’s DNA sequence contains segment ATC then it may mean that the animal may have a genetic disease. Until now scientists have found several those segments, the problem is how many kinds of DNA sequences of a species don’t contain those segments.
Suppose that DNA sequences of a species is a sequence that consist of A, C, T and G,and the length of sequences is a given integer n.
Input
First line contains two integer m (0 <= m <= 10), n (1 <= n <=2000000000). Here, m is the number of genetic disease segment, and n is the length of sequences.
Next m lines each line contain a DNA genetic disease segment, and length of these segments is not larger than 10.
Output
An integer, the number of DNA sequences, mod 100000.
Sample Input
4 3
AT
AC
AG
AA
Sample Output
36
題意:
只由 A G C T組成的長度爲n序列,有一些組合不能出現,求合法序列的數目。
思路:
這是一道很綜合的題目!
1. 建圖
在使用給定的字符串跑完AC自動機以後,實際上就可以把Trie樹當成一個有向圖。
題意可以轉換爲:從root節點(0號)開始,走n步且不會走到有序列結尾的節點的方案數。
舉個栗子,當的時候,那0的子樹的fail指針全部指向自己,每次都有4種可能,所以答案是
當序列爲 AG,CG,所得圖爲
其中2和4是結尾點,不需要考慮。
2. 狀態定義
- 定義 代表在第個點,已經走了步的方案數。
- 易得
- 轉移方程爲
- 其中代表圖中從到的邊數。如上面那個例子的矩陣爲:
- 2 1 0 1 0
1 1 0 1 0
0 0 0 0 0
1 1 0 1 0
0 0 0 0 0
3. 矩陣遞推
有沒有發現,狀態轉移方程特別像矩陣乘法?
沒看出來?
。
。
。
。
。
。
。
。
現在再看呢?
(靈魂畫手.jpg…
這個矩陣方程,和上面的遞推方程等價。
但表示成矩陣,則加速了這個遞推的過程。
4. 總結
所以到上一步就結束了。
- 我們先用AC自動機建好圖,去除結尾點(包括Fail指針能到結尾點的,因爲Fail指針對應有結尾點,意味着當前對應後綴爲結尾,那當前節點肯定不能用了),
- 然後算出點之間邊數的矩陣,
- 推出一個狀態方程,這個轉移方程可以轉換爲矩陣乘法,
- 最後再用矩陣快速冪加速遞推這個過程。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 1e6 + 7;
const int mod = 1e5;
typedef long long ll;
struct Trie {
int son[30],val,fail;
}t[maxn];
struct Matrix {
ll m[101][101];
}M;
ll m;
int n,cnt,id[505];
char s[maxn];
void init() {
for(int i = 0;i <= cnt;i++) {
memset(t[i].son,0,sizeof(t[i].son));
t[i].val = t[i].fail = 0;
}
cnt = 0;
id['A'] = 0;
id['G'] = 1;
id['C'] = 2;
id['T'] = 3;
}
void insert(char *s) {
int u = 0,len = strlen(s);
for(int i = 0;i < len;i++) {
int v = id[s[i]];
if(!t[u].son[v]) t[u].son[v] = ++cnt;
u = t[u].son[v];
}
t[u].val++;
}
void getFail() {
queue<int>q;
q.push(0);t[0].fail = 0;
while(!q.empty()) {
int u = q.front();q.pop();
if(t[t[u].fail].val) {
t[u].val = 1;
}
for(int i = 0;i < 4;i++) {
int v = t[u].son[i];
int Fail = t[u].fail;
if(!v) {
t[u].son[i] = t[Fail].son[i];continue; //直接讓不存在的兒子指向失配位置
}
if(Fail == 0 && t[Fail].son[i] == v) t[v].fail = 0;
else t[v].fail = t[Fail].son[i];
q.push(v);
}
}
}
void build() {
for(int i = 0;i <= cnt;i++) {
for(int j = 0;j < 4;j++) {
if(!t[i].val && !t[t[i].son[j]].val) {
M.m[i][t[i].son[j]]++;
}
}
}
}
Matrix mul(Matrix A,Matrix B) {
Matrix C;
memset(C.m,0,sizeof(C.m));
for(int i = 0;i <= cnt;i++) {
for(int j = 0;j <= cnt;j++) {
C.m[i][j] = 0;
for(int k = 0;k <= cnt;k++) {
C.m[i][j] = (C.m[i][j] + A.m[i][k] * B.m[k][j]) % mod;
}
}
}
return C;
}
Matrix Pow(Matrix A,ll n) {
Matrix res;
memset(res.m,0,sizeof(res.m));
for(int i = 0;i <= cnt;i++) res.m[i][i] = 1;
while(n) {
if(n & 1) {
res = mul(res,A);
}
A = mul(A,A);
n >>= 1;
}
return res;
}
int main() {
init();
scanf("%d %lld",&n,&m);
for(int i = 1;i <= n;i++) {
scanf("%s",s);
insert(s);
}
getFail();
build();
Matrix res = Pow(M,m);
ll ans = 0;
for(int i = 0;i <= cnt;i++) {
ans = (ans + res.m[0][i]) % mod;
}
printf("%lld\n",ans);
return 0;
}