POJ 2778:DNA Sequence

题目大意:给出\(m\)个仅由A,C,T,G组成的字符串和\(n\),求长度为\(n\)的所有仅由A,C,G,T组成的字符串且不包含任意一个给出字符串的个数,\(m\leq10,n\leq2000000000\)

解析:

首先发现字符串很少,我们可以对此构造出AC自动机,则问题转变为了在这个自动机上能匹配多少长度为\(n\)的字符串且不经过单词节点。

我们考虑ban掉所有的单词节点——即让它们“不可通过”,注意,如果一个节点last指针也指向单词节点,那么它也要被ban掉。

那么这个问题就可以通过DP解决,设F[i][j]为长度为j的串结束于节点i有多少种方案,可以通过每次把DP数组乘上一个转移矩阵解决,如果用矩阵快速幂,则时间复杂度为\(O(m^3logn)\)

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())

typedef long long LL;

using namespace std;

const int maxn=200+23,csize=4+1;
const int MOD=100000;

int len,n,size,cur,k,v,ch[maxn][csize],x[maxn],f[maxn],last[maxn];

LL m,ans,M[maxn][maxn],Mba[maxn][maxn];

char s[maxn];

bool word[maxn];

void insert(char *s,int n)
{
 cur=0;
 rep(i,1,n)
 {
  k=(int)(s[i]-'A'+1);
  if (!ch[cur][k]) ch[cur][k]=++size;
  cur=ch[cur][k]; 
 }
 word[cur]=1;
}

void get_fail()
{
 queue<int> Q;
 
 f[0]=0;
 rep(i,1,csize-1) if (ch[0][i]) Q.push(ch[0][i]),f[ch[0][i]]=0,last[ch[0][i]]=0;

 while (!Q.empty())
 {
  k=Q.front(); Q.pop();

  rep(i,1,csize-1)
  {
   if (!ch[k][i]) {ch[k][i]=ch[f[k]][i];continue;} else v=ch[k][i],Q.push(v);

   cur=f[k];
   while (cur && (!ch[cur][i])) cur=f[cur];

   f[v]=ch[cur][i];
   last[v]=(word[f[v]])?(f[v]):(last[f[v]]);
  }
 }
 
 rep(i,0,size) if (last[i]) word[i]=1;
}

void mult(LL c[maxn][maxn],LL a[maxn][maxn],LL b[maxn][maxn])
{
 LL temp[maxn][maxn];

 rep(i,0,size)
  rep(j,0,size)
  {
   temp[i][j]=0;
   rep(k,0,size) temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD; 
  }
 
 rep(i,0,size) rep(j,0,size) c[i][j]=temp[i][j];
}

void Pow()
{
 memset(M,0,sizeof(M));
 rep(i,0,size) M[i][i]=1;
 
 len=0;
 while (m) x[++len]=(int)(m&1),m>>=1;
 
 dep(k,len,1)
 {
  mult(M,M,M);
  if (x[k]) mult(M,M,Mba);
 }
}

int main()
{
 scanf("%d%lld",&n,&m);
 rep(i,1,n)
 {
  scanf("%s",s+1);
  len=strlen(s+1);
  
  rep(j,1,len)
  {
   if (s[j]=='C') s[j]='B';
   if (s[j]=='T') s[j]='C';
   if (s[j]=='G') s[j]='D';
  }
 
  insert(s,strlen(s+1));
 }
 
 get_fail();
 
 memset(Mba,0,sizeof(Mba));
 rep(i,0,size) if (!word[i]) rep(j,1,4) Mba[ch[i][j]][i]++;
 
 Pow();
 
 
 ans=0;
 rep(i,0,size)
  if (!word[i]) ans=(ans+M[i][0])%MOD;
 
 printf("%lld\n",ans);
 
 return 0;
}
posted @ 2016-12-13 21:29  Krew  阅读(68)  评论(0)    收藏  举报