题解 poj2778 DNA Sequence
题面
解析
先考虑这样一个问题:给定一个图((n<=100)),问从 (s) 到 (t) 的长度为 (l) 的路径数是多少.
把邻接矩阵建出来,设为 (x),答案即为 (x^l) 的对应位置.
先看 (l=2) 的情况:根据矩阵乘法有 (f_{i,j}=sumlimits_{k=1}^n f_{i,k} imes f_{k,j})
即枚举中间点 (k),将方案数加起来.
其它的也是类似的道理.
那么回到题目:考虑一个字符串在 AC自动机 上的匹配,
那答案实际上就是从根到某个节点的长度为 (n) 的路径方案数.
把 AC 自动机建出来,处理出邻接矩阵,
但要注意如果 (j) 被标记,邻接矩阵中 (f_{i,j}=0).
矩阵快速幂处理即可.
code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define ll long long
using namespace std;
inline ll read(){
ll sum=0,f=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c<='9'&&c>='0'){sum=sum*10+c-'0';c=getchar();}
return f*sum;
}
const int N=101;
const int Mod=100000;
struct mat{
ll f[N][N];
mat(){memset(f,0,sizeof(f));}
inline void init(){for(int i=0;i<N;i++) f[i][i]=1;}
};
ll n,m;
int ch[N][5],tot;
int fail[N],v[N];
char ss[N];
queue<int> que;
inline mat operator*(mat a,mat b){
mat c;
memset(c.f,0,sizeof(c.f));
for(int i=0;i<N;i++){
for(int k=0;k<N;k++){
ll ret=a.f[i][k];if(!ret) continue;
for(int j=0;j<N;j++){
if(!b.f[k][j]) continue;
c.f[i][j]=(c.f[i][j]+ret*b.f[k][j]%Mod)%Mod;
}
}
}
return c;
}
inline mat fpow(mat a,ll b){
mat ret;ret.init();
for(int i=0;i<N;i++)
for(int j=0;j<N;j++) ret.f[i][j]=(i==j);
while(b){if(b&1) ret=ret*a;a=a*a;b>>=1;}
return ret;
}
inline int id(char c){
if(c=='A') return 0;
if(c=='C') return 1;
if(c=='T') return 2;
if(c=='G') return 3;
}
inline void insert(char *s){
int len=strlen(s);
int p=0;
for(int i=0;i<len;i++){
int c=id(s[i]);
if(!ch[p][c]) ch[p][c]=++tot,memset(ch[tot],0,sizeof(ch[tot]));
p=ch[p][c];
}
v[p]=1;
}
inline void build(){
for(int i=0;i<=3;i++)
if(ch[0][i]) que.push(ch[0][i]),fail[ch[0][i]]=0;
while(!que.empty()){
int x=que.front();que.pop();
v[x]|=v[fail[x]];
for(int i=0;i<=3;i++){
if(ch[x][i]) que.push(ch[x][i]),fail[ch[x][i]]=ch[fail[x]][i];
else ch[x][i]=ch[fail[x]][i];
}
}
}
signed main(){
m=read();n=read();
for(int i=1;i<=m;i++){
scanf("%s",ss);
insert(ss);
}
build();mat a;
for(int i=0;i<=tot;i++)
for(int j=0;j<=3;j++){
if(!v[ch[i][j]]) a.f[i][ch[i][j]]++;
}
a=fpow(a,n);
ll ans=0;
for(int i=0;i<=tot;i++) ans=(ans+a.f[0][i])%Mod;
printf("%lld
",ans);
return 0;
}