#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <vector>
using namespace std;

#define REP(i, a, b) for(int i = (a); i < int(b); ++i)
#define rep(i, n) REP(i, 0, n)

template<class T> inline void chmax(T &a, const T &b) { if(a < b) a = b; }

typedef vector<vector<int>> graph;

// SCC Strongly Connected Component 強連結成分分解
struct SCC {
	int V;
	int cV;
	graph G;
	graph cG;
	vector<int> cmp;
	vector<int> vs;
	vector<bool> used;
	vector<int> size;

	SCC(int V_):V(V_), G(V_), cmp(V_), used(V_) {}
	SCC(const graph &G_):V(G_.size()), G(G_), cmp(V), used(V) {}

	void add_edge(int from, int to) {
		G[from].emplace_back(to);
	}

	void dfs(int v) {
		used[v] = true;
		for(const auto &to : G[v]) {
			if(!used[to]) dfs(to);
		}
		vs.emplace_back(v);
	}

	void rdfs(int v, int k, graph &rG) {
		used[v] = true;
		cmp[v] = k;
		for(const auto &to : rG[v]) {
			if(!used[to]) rdfs(to, k, rG);
		}
	}

	int scc() {
		vs.clear();
		fill(used.begin(), used.end(), false);

		for(int v = 0; v < V; ++v) {
			if(!used[v]) dfs(v);
		}

		graph rG(V);
		for(int v = 0; v < V; ++v) {
			for(const auto &to : G[v]) {
				rG[to].emplace_back(v);
			}
		}

		fill(used.begin(), used.end(), false);
		int k = 0;
		for(int i = static_cast<int>(vs.size()) - 1; i >= 0; --i) {
			if(!used[vs[i]]) rdfs(vs[i], k++, rG);
		}
		return k;
	}

	void construct() {
		cV = scc();
		cG.clear();
		cG.resize(cV);
		size.assign(cV, 0);

		for(int v = 0; v < V; ++v) {
			++size[cmp[v]];

			for(const auto &to : G[v]) {
				if(cmp[v] != cmp[to]) cG[cmp[v]].emplace_back(cmp[to]);
			}
		}

		for(auto &es : cG) {
			sort(es.begin(), es.end());
			es.erase(unique(es.begin(), es.end()), es.end());
		}
	}
};

enum {A, M, NUM};
vector<vector<int>> memo;

int dfs(int a, int m, const vector<SCC> &scc, const vector<vector<long long>> &flag) {
	int &res = memo[a][m];
	if(res != -1) return res;

	const bool get_point = (flag[A][a] & flag[M][m]);
	res = 0;

	if(!get_point || scc[M].size[m] > 1) {
		for(const auto &toA : scc[A].cG[a]) {
			chmax(res, dfs(toA, m, scc, flag));
		}
	}

	if(!get_point || scc[A].size[a] > 1) {
		for(const auto &toM : scc[M].cG[m]) {
			chmax(res, dfs(a, toM, scc, flag));
		} 
	}

	for(const auto &toA : scc[A].cG[a]) {
		for(const auto &toM : scc[M].cG[m]) {
			chmax(res, dfs(toA, toM, scc, flag));
		} 
	}

	res += get_point;

	return res;
}

int main() {
	cin.tie(nullptr);
	ios::sync_with_stdio(false);

	vector<SCC> scc;
	scc.reserve(NUM);

	vector<string> board(NUM);

	rep(i, NUM) {
		int n, m;
		cin >> n >> m;

		scc.emplace_back(SCC(n));
		rep(j, n) {
			char c;
			cin >> c;
			board[i] += c;
		}

		rep(j, m) {
			int from, to;
			cin >> from >> to;
			scc[i].add_edge(from - 1, to - 1);
		}

		scc[i].construct();
	}

	const int VA = scc[A].cV;
	const int VM = scc[M].cV;

	vector<vector<long long>> flag{vector<long long>(VA, 0), vector<long long>(VM, 0)};

	rep(i, NUM) {
		rep(j, scc[i].V) {
			if(islower(board[i][j])) {
				flag[i][scc[i].cmp[j]] |= (1ll << (board[i][j] - 'a'));
			}
			else {
				flag[i][scc[i].cmp[j]] |= (1ll << (board[i][j] - 'A' + 26));
			}
		}
	}

	rep(i, VA) {
		if(scc[A].size[i] == 1) continue;
		rep(j, VM) {
			if(scc[M].size[j] == 1) continue;

			if(flag[A][i] & flag[M][j]) {
				cout << -1 << endl;
				return EXIT_SUCCESS;
			}
		}
	}

	memo.resize(VA, vector<int>(VM, -1));
	int ans = 0;
	rep(i, VA) {
		rep(j, VM) {
			chmax(ans, dfs(i, j, scc, flag));
		}
	}

	cout << ans << endl;

	return EXIT_SUCCESS;
}

Measured memory_bytes: 1436