#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <cfloat>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <iostream>
#include <set>
#include <map>
#include <time.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define A 3
#define T 2
#define G 1
#define C 0


struct Info{
	char name[21];
	vector<int> V;
};

int POW[5];
int num_A,num_T,num_G,num_C,M;
char input_str[50][2001];
ll dp[2][51][51][51];
bool is_over[50];
Info info[50];

bool strCmp(char* base, char* comp){
	int length1,length2;
	for(length1=0;base[length1] != '\0';length1++);
	for(length2=0;comp[length2] != '\0';length2++);
	if(length1 != length2)return false;

	for(int i=0;base[i] != '\0'; i++){
		if(base[i] != comp[i])return false;
	}
	return true;
}

void strcpy(char* to,char* str){
	for(int i=0;str[i] != '\0';i++){
		to[i] = str[i];
		to[i+1] = '\0';
	}
}

int makeCode(char work[5]){

	int res = 0;

	for(int i = 0; work[i] != '\0'; i++){
		switch(work[i]){
		case 'A':
			res += POW[A];
			break;
		case 'T':
			res += POW[T];
			break;
		case 'G':
			res += POW[G];
			break;
		case 'C':
			res += POW[C];
			break;
		}
	}
	return res;
}

int main(){

	for(int i = 0; i < 5; i++)POW[i] = pow(2,i);

	scanf("%d %d %d %d",&num_A,&num_T,&num_G,&num_C);
	scanf("%d",&M);

	getchar();
	for(int i = 0; i < M; i++){
		fgets(input_str[i],2000,stdin);
		is_over[i] = false;
	}

	char tmp_name[21],work[5];
	int index,work_index;

	//非終端記号は、自分より前に入力された非終端記号を含まないので、逆からループ
	for(int i = M-1; i >= 0; i--){
		//非終端記号の名前の取得
		for(index = 0; input_str[i][index] != ':'; index++){
			tmp_name[index] = input_str[i][index];
		}
		tmp_name[index] = '\0';

		strcpy(info[i].name,tmp_name);

		index++; //':'の次にインデックスを進める

		while(input_str[i][index] != '\0' && input_str[i][index] != '\n'){
			while(input_str[i][index] == ' ')index++; //空白を読み飛ばす

			if(input_str[i][index] == '\0' || input_str[i][index] == '\n')break;

			if(input_str[i][index] == '['){ //終端記号

				index++;
				work_index = 0;
				while(input_str[i][index] != ']'){
					work[work_index++] = input_str[i][index];
					index++;
				}
				work[work_index] = '\0';

				info[i].V.push_back(makeCode(work)); //コードを格納

				index++;

			}else{ //非終端記号:既にinfoに情報が格納されているはずなので、特定してVをコピーする

				for(int k = 0;input_str[i][index] != '\0' && input_str[i][index] != '\n' && input_str[i][index] != ' ';k++,index++){
					tmp_name[k] = input_str[i][index];
					tmp_name[k+1] = '\0';
				}

				for(int k = i+1; k < M; k++){
					if(strCmp(info[k].name,tmp_name)){
						if(is_over[k]){
							is_over[i] = true;
							break;
						}
						for(int p = 0; p < info[k].V.size(); p++){ //AGCTコードをコピー
							info[i].V.push_back(info[k].V[p]);
							if(info[i].V.size() > num_A+num_T+num_G+num_C){
								is_over[i] = true;
								break;
							}
						}
						break;
					}
					if(is_over[i])break;
				}

				if(info[i].V.size() > num_A+num_T+num_G+num_C){
					is_over[i] = true;
				}
			}
		}
	}

	if(is_over[0] == true || info[0].V.size() != num_A+num_T+num_G+num_C){ //文字列1のコード数と、A,G,C,Tの使用回数の合計が合わなければreturn
		printf("0\n");
		return 0;
	}

	int CURRENT = 0,NEXT = 1;

	for(int a = 0; a <= num_A; a++){
		for(int t = 0; t <= num_T; t++){
			for(int g = 0; g <= num_G; g++){
				dp[CURRENT][a][t][g] = 0;
				dp[NEXT][a][t][g] = 0;
			}
		}
	}

	bool have_A,have_T,have_G,have_C;

	dp[CURRENT][0][0][0] = 1;

	for(int i = 0; i < info[0].V.size(); i++){

		//printf("i:%d\n",i);

		if(info[0].V[i] & (1 << A)){
			//printf("A\n");
			have_A = true;
		}else{
			have_A = false;
		}

		if(info[0].V[i] & (1 << T)){
			//printf("T\n");
			have_T = true;
		}else{
			have_T = false;
		}

		if(info[0].V[i] & (1 << G)){
			//printf("G\n");
			have_G = true;
		}else{
			have_G = false;
		}

		if(info[0].V[i] & (1 << C)){
			//printf("C\n");
			have_C = true;
		}else{
			have_C = false;
		}

		int c;

		for(int a = 0; a <= min(num_A,i); a++){
			for(int t = 0; t <= min(num_T,i); t++){
				if(a+t > i)break;
				for(int g = 0; g <= min(num_G,i); g++){
					if(a+t+g > i)break;
					c = i-(a+t+g);
					if(c > num_C)continue;

					if(dp[CURRENT][a][t][g] == 0)continue;

					if(have_A == true && a < num_A){
						dp[NEXT][a+1][t][g] += dp[CURRENT][a][t][g];
						dp[NEXT][a+1][t][g] %= MOD;
					}

					if(have_T == true && t < num_T){
							//printf("i:%d a;%d g:%d c:%d t:%d dp:%lld\n",i,a,g,c,t,dp[CURRENT][a][t][g]);
							dp[NEXT][a][t+1][g] += dp[CURRENT][a][t][g];
							dp[NEXT][a][t+1][g] %= MOD;
						}

					if(have_G == true && g < num_G){
						dp[NEXT][a][t][g+1] += dp[CURRENT][a][t][g];
						dp[NEXT][a][t][g+1] %= MOD;
					}

					if(have_C == true && c < num_C){
						//printf("i:%d a;%d g:%d c:%d t:%d dp:%lld\n",i,a,g,c,t,dp[CURRENT][a][t][g]);
						dp[NEXT][a][t][g] += dp[CURRENT][a][t][g];
						dp[NEXT][a][t][g] %= MOD;
					}
				}
			}
		}
		swap(NEXT,CURRENT);

		for(int a = 0; a <= num_A; a++){
			for(int t = 0; t <= num_T; t++){
				if(a+t > i)break;
				for(int g = 0; g <= num_G; g++){
					if(a+t+g > i)break;
					dp[NEXT][a][t][g] = 0;
				}
			}
		}
	}

	printf("%lld\n",dp[CURRENT][num_A][num_T][num_G]%MOD);

	return 0;
}



Measured memory_bytes: 5572