题解:
- 首先建好AC自动机
- f[i][j] 表示匹配到第 i 个字符,当前在AC自动机的编号为j的节点的方案数
- f[i][j]=∑f[i−1][k] 当且仅当 j节点,k 节点都可行
- 显然是一个线性递推,构建一个SIZ*SIZ的矩阵(SIZ为AC自动机节点数),其中a[i][j]=1当且仅当可以由i转移到j,即i和j都可行
- 考虑如何判断一个节点是否为可行状态
- 如果一个节点是病毒串的结尾,显然是不行的,打上标记
- 同样的,如果一个节点的fail有标记,即对应串的一个后缀为病毒串也是不行的,打上标记
- 至此,问题就解决了
- 复杂度 O(∑病毒串长3logn)
//by sdfzchy
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long LL;
const int inf=(1<<30),mod=100000;
int m;
LL n;
inline int in()
{
char ch=getchar();
int f=1,tmp=0;
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {tmp=(tmp<<1)+(tmp<<3)+(ch-'0');ch=getchar();}
return tmp*f;
}
char s[1100];
inline int gi(char ch)
{
if(ch=='A') return 0;
if(ch=='C') return 1;
if(ch=='T') return 2;
if(ch=='G') return 3;
}
int tot;
struct AC_atm
{
int ch[4],fail,col;
}t[210];
void ins(char *s)
{
int len=strlen(s),u=0;
for(int i=0;i<len;i++)
{
int c=gi(s[i]);
if(!t[u].ch[c]) t[u].ch[c]=++tot;
u=t[u].ch[c];
}
t[u].col=1;
}
queue<int> q;
void gi_fail()
{
for(int i=0;i<4;i++)
if(t[0].ch[i])
q.push(t[0].ch[i]),t[t[0].ch[i]].fail=0;
while(!q.empty())
{
int u=q.front(); q.pop();
t[u].col|=t[t[u].fail].col;
for(int i=0;i<4;i++)
{
int &v=t[u].ch[i];
if(v) t[v].fail=t[t[u].fail].ch[i],q.push(v);
else v=t[t[u].fail].ch[i];
}
}
}
LL ans;
struct Matrix
{
LL a[110][110];
Matrix operator *(const Matrix &b) const
{
Matrix c;
memset(c.a,0,sizeof(c.a));
for(int i=0;i<=tot;i++)
for(int j=0;j<=tot;j++)
for(int k=0;k<=tot;k++)
c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j])%mod;
return c;
}
}f;
Matrix ksm(Matrix a,LL b)
{
Matrix ret=a; b--;
while(b)
{
if(b&1) ret=ret*a;
a=a*a;
b>>=1;
}
return ret;
}
int main()
{
m=in(); scanf("%lld",&n);
for(int i=1;i<=m;i++) scanf("%s",s),ins(s);
gi_fail();
memset(f.a,0,sizeof(f.a));
for(int i=0;i<=tot;i++) if(!t[i].col)
for(int j=0;j<4;j++) if(!t[t[i].ch[j]].col)
f.a[i][t[i].ch[j]]++;
Matrix Ans=ksm(f,n);
for(int i=0;i<=tot;i++) ans=(ans+Ans.a[0][i])%mod;
printf("%lld\n",ans);
return 0;
}