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

// AOJ 2437 DNA
// 2018.4.7 bal4u

#include <stdio.h>
#include <string.h>

// 文字列のハッシュ関数
#define HASHSIZ 503
typedef struct { char *s; int id; } HASH;
HASH hash[HASHSIZ+2], *hashend = hash + HASHSIZ;

int insert(char *s, int id)
{
	unsigned long long i;
	char *p;
	HASH *tp;

	i = 0, p = s; while (*p) i = (i<<5) + *p++;
	tp = hash + (int)(i % HASHSIZ);
	while (tp->s != NULL) {
		if (!strcmp(tp->s, s)) return tp->id;
		if (++tp == hashend) tp = hash;
	}
	tp->s = s, tp->id = id;
	return -1;
}

char buf[300], *p;
int in()
{
	int n = 0;
	while (*p >= '0') n = 10*n + (*p++ & 0xf);
	p++;
	return n;
}

#define M 1000000007

int  na, nt, ng, nc, sum;
int  sz;
int  rule[52][12];
int  dp[202][52][52][52];
char text[52][300];
char tr['Z'];
int  token[250];

int together(int id)
{
	int i, j;

	for (i = 0; j = rule[id][i]; i++) {
		if (j > 0) { if (together(j)) return 1;	}
		else {
			token[sz] = -j;
			if (++sz > sum) return 1;
		}
	}
	return 0;
}

int main()
{
	int m, i, j, k, l, b, a, t, g, c;
	char *q;

	tr['A'] = 1, tr['T'] = 2, tr['G'] = 4, tr['C'] = 8;
	fgets(p=buf, 20, stdin);
	na = in(), nt = in(), ng = in(), nc = in();
	sum = na+nt+ng+nc;
	fgets(p=buf, 10, stdin), m = in();
	sz = 0;
	for (i = 0; i < m; i++) {
		fgets(p=text[i], 300, stdin);
		q = p; while (*p != ':') p++; *p = 0, p+=2;
		j = insert(q, sz);
		if (j < 0) j = sz++;
		k = 0;
		while (*p >= ' ') {
			q = p; while (*p > ' ') p++; *p++ = 0;
			if (*q != '[') {
				l = insert(q, sz);
				if (l < 0) l = sz++;
				rule[j][k++] = l;
			} else {
				b = 0;
				while (*++q != ']') b |= tr[*q];
				rule[j][k++] = -b;
			}
		}
	}
#if 0
	printf("m %d, sz %d\n", m, sz);
	for (i = 0; i < sz; i++) {
		printf("[%d] --> ", i);
		for (j = 0; rule[i][j]; j++) printf("%d ", rule[i][j]);
		printf("\n");
	}
#endif
	sz = 0;
	if (together(0) || sz != sum) { puts("0"); return 0; }
#if 0
	printf("sz %d\n", sz);
	for (i = 0; i < sz; i++) printf("[%d] %d\n", i, token[i]);
#endif

	dp[0][0][0][0] = 1;
	for (i = 0; i < sz; i++) {
		b = token[i];
		for (a = 0; a <= na; a++) for (t = 0; t <= nt; t++) for (g = 0; g <= ng; g++) {
			c = i - (a+t+g);
			if (c < 0 || c > nc) continue;
			if (b & 1) {
				dp[i+1][a+1][t][g] += dp[i][a][t][g];
				if (dp[i+1][a+1][t][g] >= M) dp[i+1][a+1][t][g] -= M;
			}
			if (b & 2) {
				dp[i+1][a][t+1][g] += dp[i][a][t][g];
				if (dp[i+1][a][t+1][g] >= M) dp[i+1][a][t+1][g] -= M;
			}
			if (b & 4) {
				dp[i+1][a][t][g+1] += dp[i][a][t][g];
				if (dp[i+1][a][t][g+1] >= M) dp[i+1][a][t][g+1] -= M;
			}
			if (b & 8) {
				dp[i+1][a][t][g] += dp[i][a][t][g];
				if (dp[i+1][a][t][g] >= M) dp[i+1][a][t][g] -= M;
			}
		}
	}
	printf("%d\n", dp[sz][na][nt][ng]);
	return 0;
}