Problem:
問題名 DNA

遺伝子は、A, T, G, C からなる文字列です。
この世界の遺伝子は奇妙なことに、ある構文規則に従うことが知られています。

構文規則は、次のような形で与えられます。

非終端記号1: 記号1_1 記号1_2 ... 記号1_n1
非終端記号2: 記号2_1 記号2_2 ... 記号2_n2
...
非終端記号m: 記号m_1 記号m_2 ... 記号m_nm

記号は非終端記号または終端記号のどちらかです。
非終端記号は小文字文字列で表され、終端記号はA, T, G, Cのうちのいくつかの文字が、"["と"]"に囲まれた文字列で表されます。

構文規則の例は次のようになります。

dna: a a b b
a: [AT]
b: [GC]

"非終端記号i: 記号i_1 記号i_2 ... 記号i_ni" を非終端記号 i のルールと呼び、ルールは、構文規則に現れる各非終端記号に対して、ちょうど 1 つづつ存在します。

文字列 s が非終端記号 i に「マッチする」とは、 s = s1 + s2 + ... + sni となるような s の部分文字列 {sj} が存在し、sj (1 ≤ j ≤ ni)がルール内の記号 j にマッチすることをいいます。

文字列 s が終端記号に「マッチする」とは、文字列が 1 文字からなり、その文字が終端記号を表す文字列に含まれることをいいます。

文字列が構文規則に従うとは、非終端記号 1 にその文字列がマッチすることをいいます。

ルール i は、記号のうちに、非終端記号 j (j ≤ i) を含みません。

構文規則と、4つの整数 Na , Nt, Ng, Nc が与えられます。
構文規則に従い、A をちょうど Na 個、T をちょうど Nt 個、G をちょうど Ng 個、C をちょうど Nc 個含むような遺伝子の総数を 1,000,000,007 で割った余りを求めなさい。

Input

Na Nt Ng Nc
m
非終端記号1: 記号 11 記号 12 ... 記号 1n1
非終端記号2: 記号 21 記号 22 ... 記号 2n2
...
非終端記号 m: 記号 m1 記号 m2 ... 記号 mnm

0 ≤ Na, Nt, Ng, Nc ≤ 50

1 ≤ m ≤ 50

1 ≤ ni ≤ 10

1 ≤ 記号を表す文字列の長さ ≤ 20 (※記号にマッチする文字列の長さではないことに注意)

Output

総数を 1,000,000,007 で割った余り

Sample Input 1

1 0 1 0
3
dna: a b
a: [AT]
b: [GC]

Output for the Sample Input 1

1

"AG"の一つです。

Sample Input 2

1 1 1 2
1
k: [ATG] [ATG] [ATG] [C] [C]

Output for the Sample Input 2

6

"ATGCC", "AGTCC", "TAGCC", "TGACC", "GATCC", "GTACC"の6つです。

Sample Input 3

3 1 1 1
3
inv: at b b b
at: [ATG] b
b: [C]

Output for the Sample Input 3

0

Solution in C++11 (judge verdict: Time Limit Exceeded)

#include<bits/stdc++.h>

using namespace std;

const string buff = "ATGC";
const int mod = 1e9 + 7;

vector< pair< int, int > > toto[50];
vector< int > order;

int dp2[52][52][52][52];
int dp3[52][52][52][52];

void dfs(int idx)
{
  for(auto &to : toto[idx]) {
    if(to.first) dfs(to.second);
    else order.push_back(to.second);
  }
}

int main()
{
  int Na, Nt, Ng, Nc, M;
  string s[50];

  cin >> Na >> Nt >> Ng >> Nc;
  int sum = Na + Nt + Ng + Nc;
  cin >> M;
  cin.ignore();
  for(int i = 0; i < M; i++) getline(cin, s[i]);

  map< string, int > convert;
  for(int i = M - 1; i >= 0; i--) {
    convert[s[i].substr(0, s[i].find(':'))] = i;
  }

  int dp[50] = {};
  for(int i = M - 1; i >= 0; i--) {
    string ss = s[i].substr(s[i].find(':') + 2);
    vector< string > splits;
    string curr;
    for(int j = 0; j <= ss.size(); j++) {
      if(j == ss.size() || ss[j] == ' ') splits.push_back(curr), curr = "";
      else curr += ss[j];
    }
    for(auto &t : splits) {
      if(t[0] == '[') {
        int mask = 0;
        for(int j = 0; j < buff.size(); j++) {
          if(t.find(buff[j]) != string::npos) {
            mask |= 1 << j;
          }
        }
        toto[i].emplace_back(0, mask);
        dp[i] = min(sum + 1, dp[i] + 1);
      } else {
        toto[i].emplace_back(1, convert[t]);
        dp[i] = min(sum + 1, dp[i] + dp[convert[t]]);
      }
    }
  }

  if(dp[0] != sum) {
    cout << 0 << endl;
  } else {
    dfs(0);

    dp2[0][0][0][0] = 1;
    for(int bit : order) {
      memset(dp3, 0, sizeof(dp3));
      for(int i = 0; i <= 50; i++) {
        for(int j = 0; j <= 50; j++) {
          for(int k = 0; k <= 50; k++) {
            for(int l = 0; l <= 50; l++) {
              if((bit >> 0) & 1) (dp3[i + 1][j][k][l] += dp2[i][j][k][l]) %= mod;
              if((bit >> 1) & 1) (dp3[i][j + 1][k][l] += dp2[i][j][k][l]) %= mod;
              if((bit >> 2) & 1) (dp3[i][j][k + 1][l] += dp2[i][j][k][l]) %= mod;
              if((bit >> 3) & 1) (dp3[i][j][k][l + 1] += dp2[i][j][k][l]) %= mod;
            }
          }
        }
      }
      swap(dp2, dp3);
    }
    cout << dp2[Na][Nt][Ng][Nc] << endl;
  }

}