Codeforces 86 C. Genetic engineering

問題

m個の塩基配列が与えられる。
n文字の塩基配列で、全ての文字が塩基配列にカバーされている
(すべてのiに対して、ある整数l≦i≦rが存在して、塩基配列のl文字目からr文字目までがいずれかの塩基配列に等しい)
ようなものの数をmod 10^9+9で求めよ。

制約条件

n≦1000
m≦10
m個の塩基配列の長さはそれぞれ10以下

方針

Aho-Corasick法でパターンマッチオートマトンを作ってDPする。
dp[pos][(最後に一致したのが何文字前か,オートマトンの状態)]を状態として、
次の文字をA,G,C,Tとして状態を変化させて遷移させる。


最後に一致した文字が10文字以上前だったら、条件が満たされることはないので次の状態を生成する必要はない。

ソースコード

struct PMA {
  PMA *next[0x100]; // next[0] is for fail
  vector<int> accept;
  PMA() { fill(next, next+0x100, (PMA*)0); }
};
PMA *buildPMA(char p[10][11], int size) {
  PMA *root = new PMA;
  for (int i = 0; i < size; ++i) { // make trie
    PMA *t = root;
    for (int j = 0; p[i][j]; ++j) {
      char c = p[i][j];
      if (t->next[c] == NULL) t->next[c] = new PMA;
      t = t->next[c];
    }
    t->accept.push_back(i);
  }
  queue<PMA*> Q; // make failure link using bfs
  for (int c = 'A'; c <= 'Z'; ++c) {
    if (root->next[c]) {
      root->next[c]->next[0] = root;
      Q.push(root->next[c]);
    } else root->next[c] = root;
  }
  while (!Q.empty()) {
    PMA *t = Q.front(); Q.pop();
    for (int c = 'A'; c <= 'Z'; ++c) {
      if (t->next[c]) {
        Q.push(t->next[c]);
        PMA *r = t->next[0];
        while (!r->next[c]) r = r->next[0];
        t->next[c]->next[0] = r->next[c];
      	fr(i,r->next[c]->accept)t->next[c]->accept.pb(*i);
      }
    }
  }
  return root;
}
int match(char *t, PMA *v, int *result) {
  int n = strlen(t);
  int count = 0;
  for (int i = 0; i < n; ++i) {
    char c = t[i];
    while (!v->next[c]) v = v->next[0];
    v = v->next[c];
    for (int j = 0; j < v->accept.size(); ++j)
      result[v->accept[j]]++;
  }
}
const int mod=(int)1e9+9;
int n,m;
map<pair<int,PMA*>,int> dp[2];
int len[10];
char in[10][11];
const char *base="ACGT";

void run(){
  cin>>n>>m;
  rep(i,m){
    cin>>in[i];
    len[i]=strlen(in[i]);
  }
  PMA* pma=buildPMA(in,m);
  dp[0][mp(0,pma)]=1;
  int cur=0,next=1;
  rep(i,n){
    dp[next].clear();
    fr(it,dp[cur]){
      rep(b,4){
        PMA *v=it->first.second;
        while(!v->next[base[b]])v=v->next[0];
        v=v->next[base[b]];
        int mxlen=-1;
        fr(jt,v->accept)mxlen=max(mxlen,len[*jt]);
        
        if(it->first.first<mxlen)(dp[next][mp(0,v)]+=it->second)%=mod;
        else if(it->first.first+1<12) (dp[next][mp(it->first.first+1,v)]+=it->second)%=mod;
      }
    }
    swap(cur,next);
  }
  
  int ans=0;
  fr(i,dp[cur])if(i->first.first==0)ans=(ans+i->second)%mod;
  cout<<ans<<endl;
}