题意:
有m(10)个,长度最大为10的DNA序列,只包含A, T, C, G,这四个字母。
这m个序列是有疾病的。
然后问,有多少种长度为n(2e9)的DNA序列,不包含以上这些带疾病的序列。
解析:
这题的fail数组终于有卵用了!
详细的解析看这篇博客:
矩阵 M[ i , j ] 表示的是从 i 到 j 只走一步有多少种走法,所以M的n次幂就代表着从 i 到 j 走 n 步有多少种走法。
代表的就是串长为 n 时,有多少种不包含以上疾病的序列。
fail 数组用来构建标记是否不含带疾病序列的矩阵。
代码:
#pragma comment(linker, "/STACK:1677721600")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <cassert>
#include <iostream>
#include <algorithm>
#define pb push_back
#define mp make_pair
#define LL long long
#define lson lo,mi,rt<<1
#define rson mi+1,hi,rt<<1|1
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a,b) memset(a,b,sizeof(a))
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
using namespace std;
const int mod = 100000;
const double eps = 1e-8;
const double ee = exp(1.0);
const int inf = 0x3f3f3f3f;
const int maxn = 100 + 10;
const double pi = acos(-1.0);
const LL iinf = 0x3f3f3f3f3f3f3f3f;
/// 矩阵快速幂
typedef vector<LL> vec;
typedef vector<vec> mat;
mat mul(mat &A, mat &B)
{
mat C(A.size(), vec(B[0].size()));
for (int i = 0; i < A.size(); i++)
{
for (int k = 0; k < B.size(); k++)
{
for (int j = 0; j < B[0].size(); j++)
{
C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod;
}
}
}
return C;
}
mat pow(mat A, LL n)
{
mat B(A.size(), vec(A.size()));
for (int i = 0; i < A.size(); i++)
{
B[i][i] = 1;
}
while (0 < n)
{
if (n & 1)
B = mul(B, A);
A = mul(A, A);
n >>= 1;
}
return B;
}
///
const int dictSize = 4;
struct Trie
{
int next[maxn * dictSize][dictSize]; //next[i][j]保存节点i的那个编号为j的节点(小写字母按字典序编号为0-(a),1-(b),2-(c),...)
int fail[maxn * dictSize]; //后缀链接 fail[j]表示节点j沿着失配指针往回走时 遇到的下一个单词节点编号
bool vis[maxn * dictSize]; //该节点是否病毒路径
int rt; //根
int nodeNum; //节点个数
void init()
{
nodeNum = 0;
rt = newNode();
}
int newNode()
{
for (int i = 0; i < dictSize; i++)
next[nodeNum][i] = -1;
vis[nodeNum++] = false;
return nodeNum - 1;
}
int get(char c)
{
if (c == 'A')
return 0;
if (c == 'T')
return 1;
if (c == 'G')
return 2;
return 3;
}
//将字符串str加入Trie前缀树中
void insert(char str[])
{
int len = strlen(str);
int now = rt;
for (int i = 0; i < len; i++)
{
if (next[now][get(str[i])] == -1)
{
next[now][get(str[i])] = newNode();
}
now = next[now][get(str[i])];
}
vis[now] = true;
}
//建立后缀链接
void build()
{
queue<int> q;
fail[rt] = rt;
for (int i = 0; i < dictSize; i++)
{
if (next[rt][i] == -1)
{
next[rt][i] = rt;
}
else
{
fail[next[rt][i]] = rt;
q.push(next[rt][i]);
}
}
while (!q.empty())
{
int now = q.front();
q.pop();
///
if (vis[fail[now]])
vis[now] = true;
///
for (int i = 0; i < dictSize; i++)
{
if (next[now][i] == -1)
{
next[now][i] = next[fail[now]][i];
}
else
{
fail[next[now][i]] = next[fail[now]][i];
q.push(next[now][i]);
}
}
}
}
mat getMat()
{
mat res(nodeNum, vec(nodeNum));
for (int i = 0; i < nodeNum; i++)
{
for (int j = 0; j < dictSize; j++)
{
if (vis[next[i][j]] == false)
{
res[i][next[i][j]]++;
}
}
}
return res;
}
void debug()
{
for (int i = 0; i < nodeNum; i++)
{
printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],vis[i]);
for(int j = 0; j < 26; j++)
printf("%2d",next[i][j]);
printf("]\n");
}
}
} ac;
char str[20];
int main()
{
#ifdef LOCAL
FIN;
#endif // LOCAL
int n, m;
while (~scanf("%d%d", &m, &n))
{
ac.init();
for (int i = 0; i < m; i++)
{
scanf("%s", str);
ac.insert(str);
}
ac.build();
mat A = ac.getMat();
A = pow(A, n);
LL ans = 0;
for (int i = 0; i < A.size(); i++)
{
ans = (ans + A[0][i]) % mod;
}
printf("%lld\n", ans);
}
return 0;
}