POJ2778
题目描述
简要题意:给出 m m m个病毒串,问你由 A T G C ATGC ATGC构成的长度为 n n n 且不包含这些病毒串的字符串有多少个?
题解
定义
d
p
[
i
]
[
j
]
=
d
p
[
当
前
长
度
为
i
]
[
当
前
在
A
C
自
动
机
的
第
j
个
节
点
]
=
方
案
数
dp[i][j]=dp[当前长度为i][当前在AC自动机的第j个节点]=方案数
dp[i][j]=dp[当前长度为i][当前在AC自动机的第j个节点]=方案数.
转移方程也十分简单.
但是注意到
n
n
n的大小,显然会T到飞起,考虑矩阵乘法优化,其实再转化一下题意就是:全图中的
0
0
0节点到其他节点刚好经过
n
n
n步的方案数(求从
A
A
A点到
B
B
B点刚好经过
n
n
n步的方案数).
那么将整幅图转化为邻接矩阵,然后对矩阵快速幂即可.
代码
未优化
#include<bits/stdc++.h>//未优化
#define M 2009
#define int long long
using namespace std;
int read(){
int f=1,re=0;char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
int fail[M],val[M],cnt,f[M][201],n,m,tr[201][4],ans;
char s[20];
void build(){
int len=strlen(s+1),u=0;
for(int i=1;i<=len;i++){
int c=0;
if(s[i]=='A') c=0;
if(s[i]=='G') c=1;
if(s[i]=='C') c=2;
if(s[i]=='T') c=3;
if(!tr[u][c]) tr[u][c]=++cnt;
u=tr[u][c];
}val[u]=1;
}
void getfail(){
queue<int>q;
for(int i=0;i<4;i++)
if(tr[0][i]) q.push(tr[0][i]);
while(q.size()){
int u=q.front();q.pop();
for(int i=0;i<4;i++){
if(!tr[u][i]) tr[u][i]=tr[fail[u]][i];
else{
int x=tr[u][i],y=fail[u];q.push(x);
while(y&&!tr[y][i]) y=fail[y];
fail[x]=tr[y][i],val[x]|=val[fail[x]];
}
}
}
}
void DP(){
f[0][0]=1,ans=0;
for(int i=0;i<n;i++)
for(int j=0;j<=cnt;j++)
if(f[i][j])
for(int k=0;k<4;k++){
int x=tr[j][k],y=val[x];
if(val[x]) continue;
f[i+1][x]+=f[i][j];
}
for(int i=0;i<=cnt;i++) ans+=f[n][i];
printf("%lld\n",ans);
}
signed main(){
m=read();n=read();
for(int i=1;i<=m;i++){
scanf("%s",s+1);
build();
}getfail();DP();
return 0;
}
已优化
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define M 209
#define int long long
using namespace std;
int read(){
int f=1,re=0;char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
int fail[M],val[M],cnt,n,m,tr[M][4],sum;
const int mod=1e5;
char s[20];
struct Matrix{
int a[102][102];
Matrix(){memset(a,0,sizeof(a));}
Matrix operator*(const Matrix &b)const{
Matrix res;
for(int i=0;i<=cnt;i++)
for(int j=0;j<=cnt;j++)
for(int k=0;k<=cnt;k++)
res.a[i][j]=(res.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
return res;
}
}base,ans;
void build(){
int len=strlen(s+1),u=0;
for(int i=1;i<=len;i++){
int c;
if(s[i]=='A') c=0;
if(s[i]=='G') c=1;
if(s[i]=='C') c=2;
if(s[i]=='T') c=3;
if(!tr[u][c]) tr[u][c]=++cnt;
u=tr[u][c];
}val[u]=1;
}
void getfail(){
queue<int>q;
for(int i=0;i<4;i++) if(tr[0][i]) q.push(tr[0][i]);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=0;i<4;i++){
if(!tr[u][i]) tr[u][i]=tr[fail[u]][i];
else{
int x=tr[u][i],y=fail[u];q.push(x);
while(y&&!tr[y][i]) y=fail[y];
fail[x]=tr[y][i];val[x]|=val[fail[x]];
}
}
}
}
void init(){
for(int i=0;i<=cnt;i++){
for(int j=0;j<4;j++){
int x=tr[i][j],y=val[x];
if(!y&&!val[i]) base.a[x][i]++;//注意此处,不能写成base.a[x][i]=1
}
}ans.a[0][0]=1;
}
void ksm(int b){
while(b){
if(b&1) ans=base*ans;
base=base*base;
b>>=1;
}
}
void DP(){
init();ksm(n);
for(int i=0;i<=cnt;i++) sum=(sum+ans.a[i][0])%mod;
printf("%lld\n",sum);
}
signed main(){
m=read();n=read();
for(int i=1;i<=m;i++){
scanf("%s",s+1);
build();
}getfail();DP();
return 0;
}