这道题目很有趣,我觉得很好玩。虽然不会做,看了题解才懂得。
刚刚开始看这道题目,以为是dp,但是所求的字符长度n的大小有20亿, 就直接放弃了。然后看了题解, 看了半天才看懂。
首先构造AC自动机, 这步就是套模板。对每个输入的字符串尾部进行标记就可以了。 但是千万注意一点,当前节点是否被标记,还要考虑其失配指针指向的节点。
很好理解的。 比如当前输入两个串, abcd, bc。 如果直接套模板的话, 只会标记“abcd”中的d节点与bc中的c节点。 可是如果我们考虑一个字符串abc的匹配度的时候, 会得到它是合法的, 但是其实它的匹配度应该是不合法的, 因为bc被包含在abcd里面,所以如果我们同时考虑每个节点的失配节点的话,就会把abcd中的c节点也一同标记了(因为abcd中的c节点的失配节点是bc中的c, 由于bc中的c被标记了,所以abcd中的c也会被标记的)。这样就对了。(为了方便理解我举了以上数据)
好了下面构造矩阵了 map[n][n],n的大小由ac自动机中的总节点数加1得到(因为考虑了根节点)。map[i][j] 表示的是ac自动机中第i个个节点到直接到第j个节点的路径数。 在AC自动机里面每个节点会衍生出四条边(分别代表A T C G ),所以在AC自动机里面跑,就可以构造出map矩阵了。(在跑路径的时候,只要下一步到达的节点不是违法节点就可以累计上去)。
这样我们就可以构造出每个节点的到下一个节点的合法路径数。再对这个矩阵求n次方(用矩阵快速幂), 再对矩阵的第.0行累加就是我们要的答案了。
解释一下,这是个图论的知识, 如果一个图上有n个点,现在你有一个矩阵表示任意两点之间一步可以走到的路径数, 对于这个矩阵求 n次方, 那么得到的矩阵的第i, j个点,就是从第i点出发,走n步最后停在j点的方法数。
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <math.h>
#include <queue>
using namespace std;
#define Max_N 200
struct Trie {
int next[Max_N][5], end1[Max_N], fail[Max_N];
int root, L;
int newnode()
{
for (int i = 0; i < 5; i++)
next[L][i] = -1;
end1[L++] = 0;
return L-1;
}
void init()
{
L = 0;
root = newnode();
}
int ex_change(char c)
{
if (c == 'A') return 0;
if (c == 'C') return 1;
if (c == 'G') return 2;
if (c == 'T') return 3;
}
void insert(char buf[])
{
int len = strlen(buf);
int now = root;
for (int i = 0; i < len; i++) {
int e = ex_change(buf[i]);
if (next[now][e] == -1)
next[now][e] = newnode();
now = next[now][e];
}
end1[now] = 1;
}
void build()
{
queue<int> Q;
fail[root] = root;
for (int i = 0; i < 4; i++)
if (next[root][i] == -1)
next[root][i] = root;
else
{
fail[next[root][i]] = root;
Q.push(next[root][i]);
}
while (!Q.empty()) {
int now = Q.front();
Q.pop();
if(end1[fail[now]]) end1[now] = true;
for (int i = 0; i < 4; 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]);
}
//end1[now] = end1[now] || end1[fail[now]];
}
}
/*for (int i = 0; i < L; i++)
end1[i] = end1[i] || end1[fail[i]];*/
}
};
#define N 150
#define Mod 100000
#define ll long long int
int n, m;
int _k;
char buf[100];
long long int map1[150][150];
Trie ac;
void Multi(ll a[][N], ll b[][N], ll c[][N]) {
for (int i=0; i<n; i++)
for (int j=0; j<n; j++) {
c[i][j] = 0;
for (int k=0; k<n; k++)
c[i][j] = (c[i][j] + a[i][k]*b[k][j]) % Mod;
}
}
//d = s
void copy(ll d[][N], ll s[][N]) {
for (int i=0; i<n; i++) for (int j=0; j<n; j++)
d[i][j] = s[i][j];
}
//a = a^b % Mod
void PowerMod(ll a[][N], ll b) {
ll t[N][N], ret[N][N];
for (int i=0; i<n; i++) ret[i][i] = 1;
while (b) {
if (b & 1) { Multi(ret, a, t); copy(ret, t); }
Multi(a, a, t); copy(a, t);
b >>= 1;
}
copy(a, ret);
}
int main()
{
while(scanf("%d%d", &_k, &m) != EOF) {
ac.init();
for (int i = 1; i <= _k; i++) {
scanf("%s", buf);
ac.insert(buf);
}
ac.build();
memset(map1, 0, sizeof(map1));
for (int i = 0; i < ac.L; i++) {
for (int j = 0; j < 4; j++) {
int u = ac.next[i][j];
if (!ac.end1[u]) map1[i][u]++;
}
}
/* for (int i = 0; i < ac.L + 1; i++)
map1[i][ac.L] = 1;*/
n = ac.L;
PowerMod(map1, m);
long long int sum = 0;
for (int i = 0; i < N; i++) {
//cout <<endl;
// printf("%d ", map1[i][j]);
sum += map1[0][i];
sum = sum % Mod;
}
cout << sum <<endl;
}
return 0;
}