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++14 (judge verdict: Accepted)

#include <bits/stdc++.h>
using namespace std;

using ll = long long;
constexpr ll MOD = 1e9+7;

int na, nt, ng, nc;
ll memo[64][64][64][64];
vector<int> terminal;
map<string, vector<string>> nonterminal;

ll rec(int a, int t, int g, int c) {
    ll& res = memo[a][t][g][c];
    if(res != -1) {
        return res;
    }
    if(a == na && t == nt && g == ng && c == nc) {
        return res = 1;
    }
    if(a > na || t > nt || g > ng || c > nc) {
        return res = 0;
    }
    res = 0;
    int term = terminal[a + t + g + c];
    if(na > a && (term >> 3) & 1) {
        res = (res + rec(a+1, t, g, c)) % MOD;
    }
    if(nt > t && (term >> 2) & 1) {
        res = (res + rec(a, t+1, g, c)) % MOD;
    }
    if(ng > g && (term >> 1) & 1) {
        res = (res + rec(a, t, g+1, c)) % MOD;
    }
    if(nc > c && (term & 1)) {
        res = (res + rec(a, t, g, c+1)) % MOD;
    }
    return res;
}

bool parse(string const& name) {
    auto terms = nonterminal[name];
    for(auto& t : terms) {
        if(t[0] == '[') {
            int symbol = 0;
            for(int i=1; i<t.size()-1; ++i) {
                if(t[i] == 'A') {
                    symbol |= (1 << 3);
                } else if(t[i] == 'T') {
                    symbol |= (1 << 2);
                } else if(t[i] == 'G') {
                    symbol |= (1 << 1);
                } else if(t[i] == 'C') {
                    symbol |= 1;
                }
            }
            terminal.push_back(symbol);
            if(terminal.size() > na + nt + ng + nc) {
                return false;
            }
        } else {
            if(!parse(t)) {
                return false;
            }
        }
    }
    return true;
}

int main() {
    fill(memo[0][0][0], memo[64][0][0], -1);
    cin >> na >> nt >> ng >> nc;
    int m;
    cin >> m;
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
    string dna;
    for(int i=0; i<m; ++i) {
        string rule;
        getline(cin, rule);
        stringstream ss(rule);
        string nonterm;
        ss >> nonterm;
        nonterm.pop_back();
        if(i == 0) {
            dna = nonterm;
        }
        string t;
        while(ss >> t) {
            nonterminal[nonterm].push_back(t);
        }
    }
    if(!parse(dna)) {
        cout << 0 << endl;
    } else {
        cout << rec(0, 0, 0, 0) << endl;
    }
}