Problem:
E: Twins Idol / ツインズアイドル
アイドル事務所841プロダクションには，多くの個性的なアイドル達が所属している． 双子のアイドルであるアキ＆マキは，841プロに所属するアイドルであり，2人ともとてもゲーム好きである． ある日2人は，次のようなゲームを自分達で自作して楽しむことにした． このゲームは，2人で協力するタイプのゲームであるため，双子の息の合ったプレイが期待される．
まず，ゲームを始める前に，2人が別々に自分の好きなようにフィールドを床に描く． フィールドは，アルファベットの1文字が書かれたいくつかのボードと，ボード間をつなぐルートで構成される． 彼女らは，このルート上を辿ることで，ボード間を移動できる． ただし，ルートには矢印がついており，その矢印の方向に向かってしか移動できない． アキのフィールドとマキのフィールドの間にルートは存在しないため，互いのフィールドを行き来することはできない．
まず，アキとマキが，自分のフィールド内の好きなボードに立つところからゲームが始まる． そして，以下の決まりに従って，2人が同時に行動していく．
2人の立っているボードの文字が等しい場合
スコアとして，1点を得る．
2人は現在立っているボードを離れて，別のボードに移動しなければならない．
ただし，別のボードに移動しようとした際，2人の内どちらかが移動できなかった場合，ゲームを終了する．
2人の立っているボードの文字が異なる場合
スコアを得ることはできない．
2人はそれぞれ，「現在のボードにとどまる」か「別のボードに移動する」かを選択して行動する．2人が同じ行動を取る必要はない．
ただし，別のボードに移動しようとした際，移動できなかった場合，ゲームを終了する．
現在以降のターンを使用して，現在のボードから，どう移動しても点数を増やせないとき
ゲームを終了する．
アキ＆マキは，このゲームでなるべく高いスコアを出したいと考えている． 2人が最大でどれだけの得点を取得できるか，841プロの新人敏腕プロデューサである君が，お得意のプログラミングによって華麗にヒントを与えてあげよう．
Input
入力は次の形式で表される．
AKI_FIELD
MAKI_FIELD
AKI_FIELD
と
MAKI_FIELD
は，それぞれアキとマキのフィールドを表す． 各フィールドの入力は，次の形式で表される．
N
M
c
1
c
2, ...,
cN
a
1
b
1
...
ai
bi
...
aM
bM
全ての数値は整数値である．
N
(1 <=
N
<= 100) はボードの数であり，各ボードには，1, 2, ...,
N
の番号が付いている．
M
(0 <=
M
<= 5000) はボード間のルートの数を表す． 次の1行には，各ボードに書かれている文字が，ボード1から順番に空白で区切られて入力される． 各文字は，アルファベットの小文字か大文字のいずれかである． 大文字と小文字は，別の文字として扱わなければならない． 続いて，
M
行にわたって，ルート情報が入力される． 各ルートは，ボード
ai
からボード
bi
に向かって伸びている(1 <=
ai
,
bi
<=
N
)．
ai
と
bi
は異なる値であり，一度入力されたルートが，再び入力されることはない． ただし，一度入力されたルートの逆方向のルートが入力されることはある．
Output
アキ＆マキが取得できる最大の得点を答えよ． ただし，無限に点数を増やせる場合は，-1を出力せよ．
Sample Input 1
5 5
a c b c a
1 2
1 3
2 3
3 4
3 5
4 3
b a c c
1 3
2 3
3 4
Sample Output 1
3
Sample Input 2
2 2
a b
1 2
2 1
4 3
a b a b
1 2
2 3
3 4
Sample Output 2
4
Sample Input 3
3 3
Y Y I
1 2
2 3
3 1
3 3
I O R
1 3
3 2
2 1
Sample Output 3
-1

Submitted solutions:

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

// Структуры для хранения графа одного поля:
struct Graph {
    int N, M;
    vector<char> letter;       // буква на каждой доске (1..N)
    vector<vector<int>> adj;   // список смежности (направленные рёбра)
};

// Чтение графа поля (N, M, буквы + рёбра)
Graph readGraph() {
    Graph g;
    cin >> g.N >> g.M;
    g.letter.resize(g.N+1);
    for(int i = 1; i <= g.N; i++){
        cin >> g.letter[i];
    }
    g.adj.assign(g.N+1, {});
    for(int i = 0; i < g.M; i++){
        int a, b;
        cin >> a >> b;
        g.adj[a].push_back(b);
    }
    return g;
}

// Глобальные объекты: поле Аки и поле Маки
Graph A_field, M_field;
int N_A, N_M;  // числа досок у Аки и Маки

// Проверка совпадения букв
bool sameLetter(int a, int m){
    return (A_field.letter[a] == M_field.letter[m]);
}

// Граф состояний: каждая вершина v соответствует (a,m).
// Индексируем их от 0 до N_A*N_M-1.
vector<vector<int>> adjState;

// Преобразование (a, m) -> v
inline int encode(int a, int m){
    // a, m по условию от 1 до N, поэтому:
    return (a - 1) * N_M + (m - 1);
}

// Обратное преобразование v -> (a, m)
inline pair<int,int> decode(int v){
    int a = v / N_M + 1;
    int m = v % N_M + 1;
    return {a, m};
}

// Построим граф состояний:
void buildStateGraph(){
    adjState.clear();
    adjState.resize(N_A * N_M);

    for(int a = 1; a <= N_A; a++){
        for(int m = 1; m <= N_M; m++){
            int v = encode(a, m);
            // Проверяем, совпадают ли буквы
            if(sameLetter(a, m)) {
                // Если совпадают, то оба ОБЯЗАНЫ перейти на другие доски
                // (a' != a) и (m' != m).
                for(int a2 : A_field.adj[a]) {
                    if(a2 == a) continue;  // другой борт
                    for(int m2 : M_field.adj[m]) {
                        if(m2 == m) continue;
                        int w = encode(a2, m2);
                        adjState[v].push_back(w);
                    }
                }
            } else {
                // Буквы разные: каждый может оставаться или перейти по ребру
                // Но если переходим, то (a2 != a) или (m2 != m).
                // Перебираем все варианты:
                vector<int> A_moves;
                // Остаёмся
                A_moves.push_back(a);
                // Или переходим
                for(int a2 : A_field.adj[a]) {
                    if(a2 != a) {
                        A_moves.push_back(a2);
                    }
                }

                vector<int> M_moves;
                M_moves.push_back(m);
                for(int m2 : M_field.adj[m]) {
                    if(m2 != m) {
                        M_moves.push_back(m2);
                    }
                }

                // Декартово произведение:
                for(int a2 : A_moves) {
                    for(int m2 : M_moves) {
                        int w = encode(a2, m2);
                        adjState[v].push_back(w);
                    }
                }
            }
        }
    }
}

// Переменные для поиска компонент сильной связности (Tarjan)
static vector<int> order, lowlink, scc_id;
static vector<bool> in_stack;
static stack<int> st;
static int timer, scc_count;

void dfs_tarjan(int v){
    order[v] = lowlink[v] = ++timer;
    st.push(v);
    in_stack[v] = true;

    for(int w : adjState[v]){
        if(order[w] == 0){
            // Не посещали w
            dfs_tarjan(w);
            lowlink[v] = min(lowlink[v], lowlink[w]);
        } else if(in_stack[w]){
            lowlink[v] = min(lowlink[v], order[w]);
        }
    }

    // Если v - корень компоненты
    if(lowlink[v] == order[v]){
        scc_count++;
        while(true){
            int topv = st.top();
            st.pop();
            in_stack[topv] = false;
            scc_id[topv] = scc_count;
            if(topv == v) break;
        }
    }
}

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

    // Считываем поля
    A_field = readGraph();
    M_field = readGraph();
    N_A = A_field.N;
    N_M = M_field.N;

    // Строим граф состояний
    buildStateGraph();
    int totalV = N_A * N_M;

    // Ищем компоненты сильной связности (Tarjan)
    order.assign(totalV, 0);
    lowlink.assign(totalV, 0);
    scc_id.assign(totalV, 0);
    in_stack.assign(totalV, false);
    while(!st.empty()) st.pop();

    timer = 0;
    scc_count = 0;

    for(int v = 0; v < totalV; v++){
        if(order[v] == 0){
            dfs_tarjan(v);
        }
    }

    // sizeSCC[c] = количество вершин в компоненте c
    vector<int> sizeSCC(scc_count + 1, 0);
    for(int v = 0; v < totalV; v++){
        sizeSCC[scc_id[v]]++;
    }

    // Проверяем условие бесконечности
    bool infinite = false;
    for(int v = 0; v < totalV; v++){
        int comp = scc_id[v];
        // Если в КСС размер > 1, там есть цикл
        // Или проверим самопетлю, если size=1
        if(sizeSCC[comp] > 1){
            // Цикл точно есть
            auto [a,m] = decode(v);
            // Если среди вершин КСС есть хоть одно состояние (a,m) с совпадающими буквами => infinite
            if(sameLetter(a,m)){
                infinite = true;
                break;
            }
        } else {
            // sizeSCC[comp] == 1
            // Проверяем самопетли
            for(int w : adjState[v]){
                if(w == v){
                    // самопетля
                    auto [a,m] = decode(v);
                    if(sameLetter(a,m)){
                        infinite = true;
                        break;
                    }
                }
            }
            if(infinite) break;
        }
    }

    if(infinite){
        cout << -1 << "\n";
        return 0;
    }

    // Если бесконечности нет, ищем максимум очков.
    // dp[v] = максимум очков, начиная из v.
    vector<int> dp(totalV, -1);

    function<int(int)> dfs_dp = [&](int v) -> int {
        if(dp[v] != -1) return dp[v];
        dp[v] = 0;

        auto [a,m] = decode(v);
        // Если в состоянии (a,m) буквы совпадают, то +1 очко
        // (Независимо от того, есть ли переход или нет, т.к. по условию
        // очко "сразу" получают, а если перейти нельзя, игра заканчивается,
        // но очко не отменяется.)
        int addScore = sameLetter(a,m) ? 1 : 0;

        int bestNext = 0;
        for(int w : adjState[v]){
            bestNext = max(bestNext, dfs_dp(w));
        }
        dp[v] = addScore + bestNext;
        return dp[v];
    };

    int ans = 0;
    for(int v = 0; v < totalV; v++){
        ans = max(ans, dfs_dp(v));
    }
    cout << ans << "\n";

    return 0;
}
accuracy: 0.8214285714285714
time_ms: 160
memory_kb: 143904
final_rank: 11

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

// Структуры для хранения графа одного поля:
struct Graph {
    int N, M;
    vector<char> letter;            // буква на каждой доске (1..N), индекс 0 не используем
    vector<vector<int>> adj;        // список смежности
};

// Чтение графа поля (N, M, буквы + ребра)
Graph readGraph() {
    Graph g;
    cin >> g.N >> g.M;
    g.letter.resize(g.N+1);
    for(int i = 1; i <= g.N; i++){
        cin >> g.letter[i];
    }
    g.adj.assign(g.N+1, {});
    for(int i = 0; i < g.M; i++){
        int a,b;
        cin >> a >> b;
        g.adj[a].push_back(b);
    }
    return g;
}

// Глобальные объекты полей
Graph A, M;  // поле Аки и поле Маки

// Будем строить граф состояний (a,m). Максимум N*N вершин (1..N_A)*(1..N_M).
// Чтобы однозначно нумеровать пары, заведём функцию id(a,m).
// id(a,m) = (a-1)*M_N + (m-1), где M_N = M.N, например.
// Или сразу сделаем двумерный массив.

static const int MAXN = 100; // по условию не более 100
int N_A, N_M;                // числа досок у Аки и у Маки

// Проверка совпадения букв: true, если A.letter[a] == M.letter[m]
bool sameLetter(int a, int m){
    return (A.letter[a] == M.letter[m]);
}

// Для построения общего графа: список смежности size (N_A*N_M).
// Каждая вершина v соответствует паре (a, m).
// adjState[v] = список вершин, куда можно перейти из (a,m).
vector<vector<int>> adjState;

// Функция, переводящая (a,m) в номер вершины v:
inline int encode(int a, int m){
    return (a-1)*N_M + (m-1);
}

// Обратное декодирование (для удобства в циклах), если нужно:
inline pair<int,int> decode(int v){
    int a = v / N_M + 1;
    int m = v % N_M + 1;
    return {a,m};
}

// Построим граф состояний:
void buildStateGraph(){
    adjState.clear();
    adjState.resize(N_A * N_M);

    for(int a = 1; a <= N_A; a++){
        for(int m = 1; m <= N_M; m++){
            int v = encode(a,m);
            // Проверяем, совпадают ли буквы
            if(sameLetter(a,m)){
                // ОБЯЗАТЕЛЬНО оба должны перейти на другие доски
                // a' != a, m' != m, плюс ребро a->a', m->m'
                for(int a2 : A.adj[a]){
                    if(a2 == a) continue;
                    // хотя по условию a2 != a явно, но проверим
                    if(a2 == a) continue;
                    for(int m2 : M.adj[m]){
                        if(m2 == m) continue;
                        int w = encode(a2, m2);
                        adjState[v].push_back(w);
                    }
                }
            } else {
                // Буквы разные: каждый может оставаться или переходить
                // Но если переходим, то a2 != a, m2 != m
                // при условии, что есть ребро.
                // Перечислим все варианты
                vector<int> A_moves;
                // либо остаёмся, либо все доступные вершины
                A_moves.push_back(a);
                for(int a2 : A.adj[a]){
                    if(a2 != a) A_moves.push_back(a2);
                }
                vector<int> M_moves;
                M_moves.push_back(m);
                for(int m2 : M.adj[m]){
                    if(m2 != m) M_moves.push_back(m2);
                }
                // Декартово произведение
                for(int a2 : A_moves){
                    for(int m2 : M_moves){
                        // (a2,m2) - возможное следующее состояние
                        // Нет отдельного очка, но переход разрешён
                        int w = encode(a2, m2);
                        adjState[v].push_back(w);
                    }
                }
            }
        }
    }
}

// Дальше ищем в этом графе компоненты сильной связности.
// Если найдём такую КСС (SCC), в которой присутствует хотя бы одна вершина (a,m)
// c одинаковыми буквами, и эта КСС содержит цикл (то есть либо >1 вершины, либо петля на себя),
// выводим -1.

// Переменные для Tarjan
vector<int> order, lowlink, scc_id;
vector<bool> in_stack;
stack<int> st;
int timer, scc_count;

void dfs_tarjan(int v){
    order[v] = lowlink[v] = ++timer;
    st.push(v);
    in_stack[v] = true;

    for(int w : adjState[v]){
        if(order[w] == 0){
            dfs_tarjan(w);
            lowlink[v] = min(lowlink[v], lowlink[w]);
        } else if(in_stack[w]){
            lowlink[v] = min(lowlink[v], order[w]);
        }
    }

    // Если v - корень компоненты
    if(lowlink[v] == order[v]){
        // Выгружаем из стека всю компоненту
        scc_count++;
        while(true){
            int topv = st.top();
            st.pop();
            in_stack[topv] = false;
            scc_id[topv] = scc_count;
            if(topv == v) break;
        }
    }
}

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

    // Считываем поля
    A = readGraph();
    M = readGraph();
    N_A = A.N;
    N_M = M.N;

    // Строим граф состояний
    buildStateGraph();
    int totalV = N_A * N_M;

    // Ищем КСС (Tarjan)
    order.assign(totalV, 0);
    lowlink.assign(totalV, 0);
    scc_id.assign(totalV, 0);
    in_stack.assign(totalV, false);

    timer = 0;
    scc_count = 0;

    for(int v = 0; v < totalV; v++){
        if(order[v] == 0){
            dfs_tarjan(v);
        }
    }

    // Проверим каждую КСС: есть ли в ней цикл и есть ли совпадающая буква
    // КСС содержит цикл, если в ней >1 вершины или есть петля (v->v).
    // Для удобства: посчитаем размеры КСС, и проверим самопетли.
    vector<int> sizeSCC(scc_count+1, 0);
    for(int v = 0; v < totalV; v++){
        sizeSCC[scc_id[v]]++;
    }

    // Флаг, указывающий на бесконечные очки
    bool infinite = false;

    // Проверяем каждую вершину, есть ли петля v->v и входит ли v
    // в компоненту, где sizeSCC > 1 или есть самопетля:
    for(int v = 0; v < totalV; v++){
        int comp = scc_id[v];
        if(sizeSCC[comp] > 1) {
            // В этой компоненте уже точно есть цикл
            // Если среди этой компоненты есть хоть одна (a,m) с совпадающими буквами, infinite = true
            // Проверяем
            auto [a,m2] = decode(v);
            if(sameLetter(a,m2)) {
                infinite = true;
                break;
            }
        }
        // Если в компоненте одна вершина, проверим самопетлю
        if(sizeSCC[comp] == 1){
            // Проверяем рёбра из v в v
            for(int w : adjState[v]){
                if(w == v){
                    // самопетля
                    auto [a,m2] = decode(v);
                    if(sameLetter(a,m2)){
                        infinite = true;
                        break;
                    }
                }
            }
            if(infinite) break;
        }
    }

    if(infinite){
        cout << -1 << "\n";
        return 0;
    }

    // Если бесконечности нет, считаем максимальное количество очков.
    // Нам нужно dp[v], где dp[v] = максимум очков, начиная из вершины v.
    // Если в (a,m) буквы совпадают, при "входе" в этот ход получается +1 очко.

    vector<int> dp(totalV, -1);
    function<int(int)> dfs_dp = [&](int v){
        if(dp[v] != -1) return dp[v];
        dp[v] = 0;  // минимум 0
        // Вычислим dp[v]
        // Если в v совпадают буквы -> +1 при переходе
        bool match = false;
        {
            auto [a,m2] = decode(v);
            match = sameLetter(a,m2);
        }
        int addScore = (match ? 1 : 0);

        // Переходим по всем рёбрам
        int bestNext = 0; // если нет ходов, останется 0
        if(!adjState[v].empty()){
            for(int w : adjState[v]){
                bestNext = max(bestNext, dfs_dp(w));
            }
        }
        dp[v] = addScore + bestNext;
        return dp[v];
    };

    int ans = 0;
    for(int v = 0; v < totalV; v++){
        ans = max(ans, dfs_dp(v));
    }
    cout << ans << "\n";

    return 0;
}
accuracy: 0.8214285714285714
time_ms: 160
memory_kb: 143672
final_rank: 10

code_snippet: #include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <random>
#include <cassert>
#include <bitset>
#include <numeric>
#include <queue>
#include <unordered_map>
#include <climits>
#include <array>
#include <memory>
#include <iomanip>
#include <stack>
#include <set>

std::vector<int> group(const std::vector<std::vector<int>>& graph) {
	const auto size{ graph.size()};
	std::vector<int> order(size, -1);
	std::stack<int> stack;
	std::vector<bool> visit(size, false);
	int count{ 0 };
	for (auto i = 0; i < size; ++i) {
		if (order[i] >= 0) continue;
		stack.push(i);
		while (!stack.empty()) {
			auto current{ stack.top() };
			stack.pop();
			if (current >= 0) {
				if (visit[current]) continue;
				visit[current] = true;
				stack.push(~current);
				for (const auto next : graph[current]) {
					if (visit[next]) continue;
					stack.push(next);
				}
			}
			else {
				current = ~current;
				order[current] = count++;
			}
		}
	}
	std::vector<std::vector<int>> reverse(size);
	for (auto i = 0; i < size; ++i) {
		for (const auto j : graph[i]) {
			reverse[j].push_back(i);
		}
	}
	std::vector<int> indices(size); std::iota(indices.begin(), indices.end(), 0);
	std::sort(indices.rbegin(), indices.rend(), [&order](const auto i, const auto j) {return order[i] < order[j]; });
	std::vector<int> result(size, -1);
	count = 0;
	for (const auto i : indices) {
		if (result[i] >= 0) continue;
		stack.push(i);
		result[i] = count;
		while (!stack.empty()) {
			const auto current{ stack.top() };
			stack.pop();
			for (const auto next : reverse[current]) {
				if (result[next] >= 0) continue;
				result[next] = count;
				stack.push(next);
			}
		}
		count += 1;
	}
	return result;
}

int solve(const std::vector<int> a, const std::vector<std::vector<int>> aGraph, const std::vector<int> b, const std::vector<std::vector<int>> bGraph) {
	const auto aGroup{ group(aGraph) };
	const auto bGroup{ group(bGraph) };
	const auto aSize{ (*std::max_element(aGroup.begin(), aGroup.end())) + 1 };
	const auto bSize{ (*std::max_element(bGroup.begin(), bGroup.end())) + 1 };
	std::vector<std::vector<int>> aGroupIndices(aSize), bGroupIndices(bSize);
	for (auto i = 0; i < aGroup.size(); ++i) {
		aGroupIndices[aGroup[i]].push_back(i);
	}
	for (auto i = 0; i < bGroup.size(); ++i) {
		bGroupIndices[bGroup[i]].push_back(i);
	}
	std::vector<bool> inA(52, false), inB(52, false);
	for (const auto& indices : aGroupIndices) {
		if (indices.size() <= 1) continue;
		for (const auto i : indices) {
			inA[a[i]] = true;
		}
	}
	for (const auto& indices : bGroupIndices) {
		if (indices.size() <= 1) continue;
		for (const auto i : indices) {
			inB[b[i]] = true;
		}
	}

	for (auto i = 0; i < 52; ++i) {
		if (inA[i] && inB[i]) return -1;
	}

	std::vector<std::vector<int>> aDAG(aSize), bDAG(bSize);
	std::vector<std::bitset<52>> aChar(aSize), bChar(bSize);
	for (auto i = 0; i < aGraph.size(); ++i) {
		const auto iGroup{ aGroup[i] };
		aChar[iGroup].set(a[i]);
		for (const auto j : aGraph[i]) {
			const auto jGroup{aGroup[j]};
			if (iGroup == jGroup) continue;
			aDAG[iGroup].push_back(jGroup);
		}
	}
	for (auto i = 0; i < bGraph.size(); ++i) {
		const auto iGroup{ bGroup[i] };
		bChar[iGroup].set(b[i]);
		for (const auto j : bGraph[i]) {
			const auto jGroup{ bGroup[j] };
			if (iGroup == jGroup) continue;
			bDAG[iGroup].push_back(jGroup);
		}
	}

	std::vector<int> inDegree(aSize * bSize, 0);
	std::vector<std::vector<int>> graph(aSize * bSize), forceMove(aSize * bSize);
	std::vector<bool> isCycleA(aSize, false), isCycleB(bSize, false);
	std::transform(aGroupIndices.begin(), aGroupIndices.end(), isCycleA.begin(), [](const auto& vec) {return vec.size() > 1; });
	std::transform(bGroupIndices.begin(), bGroupIndices.end(), isCycleB.begin(), [](const auto& vec) {return vec.size() > 1; });
	std::vector<bool> canScore(aSize * bSize);
	for (auto i = 0; i < aSize; ++i) {
		for (auto j = 0; j < bSize; ++j) {
			canScore[i * bSize + j] = (aChar[i] & bChar[j]).any();
			for (const auto k : aDAG[i]) {
				graph[i * bSize + j].push_back(k * bSize + j);
				if (isCycleB[j]) {
					forceMove[i * bSize + j].push_back(k * bSize + j);
				}
			}
			for (const auto k : bDAG[j]) {
				graph[i * bSize + j].push_back(i * bSize + k);
				if (isCycleA[i]) {
					forceMove[i * bSize + j].push_back(i * bSize + k);
				}
			}
			for (const auto ak : aDAG[i]) {
				for (const auto bk : bDAG[j]) {
					graph[i * bSize + j].push_back(ak * bSize + bk);
					forceMove[i * bSize + j].push_back(ak * bSize + bk);
				}
			}
		}
	}
	for (const auto &edges: graph) {
		for (const auto j : edges) {
			inDegree[j] += 1;
		}
	}

	std::vector<int> maxCount(aSize * bSize, 0);
	std::stack<int> stack; 
	for (auto i = 0; i < inDegree.size(); ++i) {
		if (inDegree[i] == 0) stack.push(i);
	}
	while (!stack.empty()) {
		const auto current{ stack.top() }; stack.pop();
		for (const auto next : graph[current]) {
			maxCount[next] = std::max(maxCount[next], maxCount[current]);
			if (--inDegree[next] == 0) stack.push(next);
		}
		if (canScore[current]) {
			maxCount[current] += 1;
			for (const auto next : forceMove[current]) {
				maxCount[next] = std::max(maxCount[next], maxCount[current]);
			}
		}
	}
	return *std::max_element(maxCount.begin(), maxCount.end());
}


int main() {
	std::unordered_map<char, int> charMap;
	for (auto c = 'a'; c <= 'z'; ++c) {
		const auto upper{ std::toupper(c) };
		charMap[c] = charMap.size();
		charMap[upper] = charMap.size();
	}
	int n, m; 
	std::cin >> n >> m;
	std::vector<int> a(n);
	for (auto& v : a) {
		char c;
		std::cin >> c;
		v = charMap.at(c);
	}
	std::vector<std::vector<int>> graphA(n);
	for (auto i = 0; i < m; ++i) {
		int a, b; std::cin >> a >> b; --a; --b;
		graphA[a].push_back(b);
	}
	std::cin >> n >> m;
	std::vector<int> b(n);
	for (auto& v : b) {
		char c;
		std::cin >> c;
		v = charMap.at(c);
	}
	std::vector<std::vector<int>> graphB(n);
	for (auto i = 0; i < m; ++i) {
		int a, b; std::cin >> a >> b; --a; --b;
		graphB[a].push_back(b);
	}
	const auto result{ solve(a, graphA, b, graphB) };
	std::cout << result << '\n';
}
accuracy: 1
time_ms: 240
memory_kb: 244820
final_rank: 8

code_snippet: #include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <algorithm>
#include <bitset>
#include <complex>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <cassert>
#include <functional>

typedef long long ll;
using namespace std;

#ifndef LOCAL
#define debug(...) ;
#else
#define debug(...) cerr << __LINE__ << " : " << #__VA_ARGS__ << " = " << _tostr(__VA_ARGS__) << endl;

template<typename T>
ostream &operator<<(ostream &out, const vector<T> &v);

template<typename T1, typename T2>
ostream &operator<<(ostream &out, const pair<T1, T2> &p) {
  out << "{" << p.first << ", " << p.second << "}";
  return out;
}

template<typename T>
ostream &operator<<(ostream &out, const vector<T> &v) {
  out << '{';
  for (const T &item : v) out << item << ", ";
  out << "\b\b}";
  return out;
}

void _tostr_rec(ostringstream &oss) {
  oss << "\b\b \b";
}

template<typename Head, typename... Tail>
void _tostr_rec(ostringstream &oss, Head &&head, Tail &&... tail) {
  oss << head << ", ";
  _tostr_rec(oss, forward<Tail>(tail)...);
}

template<typename... T>
string _tostr(T &&... args) {
  ostringstream oss;
  int size = sizeof...(args);
  if (size > 1) oss << "{";
  _tostr_rec(oss, forward<T>(args)...);
  if (size > 1) oss << "}";
  return oss.str();
}
#endif

#define mod 1000000007 //1e9+7(prime number)
#define INF 1000000000 //1e9
#define LLINF 2000000000000000000LL //2e18
#define SIZE 110

/* Strongly Connected Component */

/*
 1. dfsをして、戻るときに1から順に番号を付ける
 2. 数値が大きいノードから逆辺を使ってdfsをする。
    すでに訪れているノードには行かない。
    たどり着けるノードが同じ連結成分に属する。
 */

struct SCC {
  int n;
  vector<vector<short>> G, rG;
  vector<int> vs, cmp;
  vector<bool> used;

  SCC(int n): n(n), G(n), rG(n), cmp(n) {}

  void add_edge(short from, short to) {
    G[from].push_back(to);
    rG[to].push_back(from);
  }

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

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

  int solve() {
    used.assign(n, false);
    vs.clear();
    for (int i = 0; i < n; i++)
      if (!used[i]) dfs(i);
    used.assign(n, false);
    int k = 0;
    for (int i = (int)vs.size() - 1; i >= 0; i--)
      if (!used[vs[i]]) rdfs(vs[i], k++);
    return k; //強連結成分数
  }

  //属する強連結成分番号(トポロジカル順)
  int operator[](int k) const {
    return cmp[k];
  }
};

struct User {
  int N, M;
  char C[SIZE];
  vector<int> G[SIZE];

  void scan() {
    scanf("%d%d", &N, &M);
    for (int i = 0; i < N; i++) {
      scanf(" %c", C + i);
    }
    for (int i = 0; i < M; i++) {
      int a, b;
      scanf("%d%d", &a, &b);
      G[a - 1].push_back(b - 1);
    }
  }
};

int main() {
  User A, B;

  A.scan();
  B.scan();

  SCC scc(A.N * B.N);

  auto isSame = [&](int x) {
    return A.C[x / B.N] == B.C[x % B.N];
  };

  for (int i = 0; i < A.N; i++) {
    for (int j = 0; j < B.N; j++) {
      int from = i * B.N + j;

      for (int a : A.G[i]) {
        for (int b : B.G[j]) {
          int to = a * B.N + b;
          scc.add_edge(from, to);
        }
      }
      if (!isSame(from)) {
        for (int a : A.G[i]) {
          scc.add_edge(from, a * B.N + j);
        }
        for (int b : B.G[j]) {
          scc.add_edge(from, i * B.N + b);
        }
      }
    }
  }

  scc.solve();

  vector<pair<int, int>> vec;
  int N = A.N * B.N;

  for (int i = 0; i < N; i++) {
    vec.push_back({scc[i], i});
  }

  sort(vec.begin(), vec.end());

  debug(vec);

  int prevG = -1;
  bool ok = false, isPositive = false;

  for (auto p : vec) {
    if (prevG != p.first) {
      isPositive = false;
    }
    ok |= isPositive;
    isPositive |= isSame(p.second);
    if (prevG == p.first) {
      ok |= isPositive;
    }
    prevG = p.first;
  }

  if (ok) {
    puts("-1");
    return 0;
  }

  vector<int> dp(N, 0), score(N, 0);

  for (int i = 0; i < N; i++) {
    if (isSame(i)) {
      score[scc[i]] = 1;
      dp[scc[i]] = 1;
    }
  }

  int ans = 0;

  for (auto v : vec) {
    int i = v.first;
    for (short s : scc.G[v.second]) {
      int to = scc[s];
      if (to == i) continue;
      dp[to] = max(dp[to], dp[i] + score[to]);
      debug(i, to, score[to]);
    }
    ans = max(ans, dp[i]);
  }

  cout << ans << endl;

  return 0;
}
accuracy: 1
time_ms: 480
memory_kb: 173284
final_rank: 9

code_snippet: #include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <algorithm>
#include <bitset>
#include <complex>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <cassert>
#include <functional>

typedef long long ll;
using namespace std;

#ifndef LOCAL
#define debug(...) ;
#else
#define debug(...) cerr << __LINE__ << " : " << #__VA_ARGS__ << " = " << _tostr(__VA_ARGS__) << endl;

template<typename T>
ostream &operator<<(ostream &out, const vector<T> &v);

template<typename T1, typename T2>
ostream &operator<<(ostream &out, const pair<T1, T2> &p) {
  out << "{" << p.first << ", " << p.second << "}";
  return out;
}

template<typename T>
ostream &operator<<(ostream &out, const vector<T> &v) {
  out << '{';
  for (const T &item : v) out << item << ", ";
  out << "\b\b}";
  return out;
}

void _tostr_rec(ostringstream &oss) {
  oss << "\b\b \b";
}

template<typename Head, typename... Tail>
void _tostr_rec(ostringstream &oss, Head &&head, Tail &&... tail) {
  oss << head << ", ";
  _tostr_rec(oss, forward<Tail>(tail)...);
}

template<typename... T>
string _tostr(T &&... args) {
  ostringstream oss;
  int size = sizeof...(args);
  if (size > 1) oss << "{";
  _tostr_rec(oss, forward<T>(args)...);
  if (size > 1) oss << "}";
  return oss.str();
}
#endif

#define mod 1000000007 //1e9+7(prime number)
#define INF 1000000000 //1e9
#define LLINF 2000000000000000000LL //2e18
#define SIZE 110

/* Strongly Connected Component */

/*
 1. dfsをして、戻るときに1から順に番号を付ける
 2. 数値が大きいノードから逆辺を使ってdfsをする。
    すでに訪れているノードには行かない。
    たどり着けるノードが同じ連結成分に属する。
 */

struct SCC {
  int n;
  vector<vector<short>> G, rG;
  vector<int> vs, cmp;
  vector<bool> used;

  SCC(int n): n(n), G(n), rG(n), cmp(n) {}

  void add_edge(short from, short to) {
    G[from].push_back(to);
    rG[to].push_back(from);
  }

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

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

  int solve() {
    used.assign(n, false);
    vs.clear();
    for (int i = 0; i < n; i++)
      if (!used[i]) dfs(i);
    used.assign(n, false);
    int k = 0;
    for (int i = (int)vs.size() - 1; i >= 0; i--)
      if (!used[vs[i]]) rdfs(vs[i], k++);
    return k; //強連結成分数
  }

  //属する強連結成分番号(トポロジカル順)
  int operator[](int k) const {
    return cmp[k];
  }
};

struct User {
  int N, M;
  char C[SIZE];
  vector<int> G[SIZE];

  void scan() {
    scanf("%d%d", &N, &M);
    for (int i = 0; i < N; i++) {
      scanf(" %c", C + i);
    }
    for (int i = 0; i < M; i++) {
      int a, b;
      scanf("%d%d", &a, &b);
      G[a - 1].push_back(b - 1);
    }
  }
};

int main() {
  User A, B;

  A.scan();
  B.scan();

  SCC scc(A.N * B.N);

  auto isSame = [&](int x) {
    return A.C[x / B.N] == B.C[x % B.N];
  };

  for (int i = 0; i < A.N; i++) {
    for (int j = 0; j < B.N; j++) {
      int from = i * B.N + j;

      for (int a : A.G[i]) {
        for (int b : B.G[j]) {
          int to = a * B.N + b;
          scc.add_edge(from, to);
        }
      }
      if (!isSame(from)) {
        for (int a : A.G[i]) {
          scc.add_edge(from, a * B.N + j);
        }
        for (int b : B.G[j]) {
          scc.add_edge(from, i * B.N + b);
        }
      }
    }
  }

  scc.solve();

  vector<pair<int, int>> vec;
  int N = A.N * B.N;

  for (int i = 0; i < N; i++) {
    vec.push_back({scc[i], i});
  }

  sort(vec.begin(), vec.end());

  int prevG = -1;
  bool ok = false, isPositive = false;

  for (auto p : vec) {
    if (prevG != p.first) {
      isPositive = false;
    }
    prevG = p.first;
    ok |= isPositive;
    isPositive |= isSame(p.second);
  }

  if (ok) {
    puts("-1");
    return 0;
  }

  vector<int> dp(N, 0), score(N, 0);

  for (int i = 0; i < N; i++) {
    if (isSame(i)) {
      score[scc[i]] = 1;
      dp[scc[i]] = 1;
    }
  }

  int ans = 0;

  for (auto v : vec) {
    int i = v.first;
    for (short s : scc.G[v.second]) {
      int to = scc[s];
      if (to == i) continue;
      dp[to] = max(dp[to], dp[i] + score[to]);
      debug(i, to, score[to]);
    }
    ans = max(ans, dp[i]);
  }

  cout << ans << endl;

  return 0;
}
accuracy: 0.7321428571428571
time_ms: 470
memory_kb: 173212
final_rank: 12

code_snippet: #include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <algorithm>
#include <bitset>
#include <complex>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <cassert>
#include <functional>

typedef long long ll;
using namespace std;

#ifndef LOCAL
#define debug(...) ;
#else
#define debug(...) cerr << __LINE__ << " : " << #__VA_ARGS__ << " = " << _tostr(__VA_ARGS__) << endl;

template<typename T>
ostream &operator<<(ostream &out, const vector<T> &v);

template<typename T1, typename T2>
ostream &operator<<(ostream &out, const pair<T1, T2> &p) {
  out << "{" << p.first << ", " << p.second << "}";
  return out;
}

template<typename T>
ostream &operator<<(ostream &out, const vector<T> &v) {
  out << '{';
  for (const T &item : v) out << item << ", ";
  out << "\b\b}";
  return out;
}

void _tostr_rec(ostringstream &oss) {
  oss << "\b\b \b";
}

template<typename Head, typename... Tail>
void _tostr_rec(ostringstream &oss, Head &&head, Tail &&... tail) {
  oss << head << ", ";
  _tostr_rec(oss, forward<Tail>(tail)...);
}

template<typename... T>
string _tostr(T &&... args) {
  ostringstream oss;
  int size = sizeof...(args);
  if (size > 1) oss << "{";
  _tostr_rec(oss, forward<T>(args)...);
  if (size > 1) oss << "}";
  return oss.str();
}
#endif

#define mod 1000000007 //1e9+7(prime number)
#define INF 1000000000 //1e9
#define LLINF 2000000000000000000LL //2e18
#define SIZE 110

/* Strongly Connected Component */

/*
 1. dfsをして、戻るときに1から順に番号を付ける
 2. 数値が大きいノードから逆辺を使ってdfsをする。
    すでに訪れているノードには行かない。
    たどり着けるノードが同じ連結成分に属する。
 */

struct SCC {
  int n;
  vector<vector<short>> G, rG;
  vector<int> vs, cmp;
  vector<bool> used;

  SCC(int n): n(n), G(n), rG(n), cmp(n) {}

  void add_edge(short from, short to) {
    G[from].push_back(to);
    rG[to].push_back(from);
  }

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

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

  int solve() {
    used.assign(n, false);
    vs.clear();
    for (int i = 0; i < n; i++)
      if (!used[i]) dfs(i);
    used.assign(n, false);
    int k = 0;
    for (int i = (int)vs.size() - 1; i >= 0; i--)
      if (!used[vs[i]]) rdfs(vs[i], k++);
    return k; //強連結成分数
  }

  //属する強連結成分番号(トポロジカル順)
  int operator[](int k) const {
    return cmp[k];
  }
};

struct User {
  int N, M;
  char C[SIZE];
  vector<int> G[SIZE];

  void scan() {
    scanf("%d%d", &N, &M);
    for (int i = 0; i < N; i++) {
      scanf(" %c", C + i);
    }
    for (int i = 0; i < M; i++) {
      int a, b;
      scanf("%d%d", &a, &b);
      G[a - 1].push_back(b - 1);
    }
  }
};

int main() {
  User A, B;

  A.scan();
  B.scan();

  SCC scc(A.N * B.N);

  auto isSame = [&](int x) {
    return A.C[x / B.N] == B.C[x % B.N];
  };

  for (int i = 0; i < A.N; i++) {
    for (int j = 0; j < B.N; j++) {
      int from = i * B.N + j;

      for (int a : A.G[i]) {
        for (int b : B.G[j]) {
          int to = a * B.N + b;
          scc.add_edge(from, to);
        }
      }
      if (!isSame(from)) {
        for (int a : A.G[i]) {
          scc.add_edge(from, a * B.N + j);
        }
        for (int b : B.G[j]) {
          scc.add_edge(from, i * B.N + b);
        }
      }
    }
  }

  scc.solve();

  vector<pair<int, int>> vec;
  int N = A.N * B.N;

  for (int i = 0; i < N; i++) {
    vec.push_back({scc[i], i});
  }

  sort(vec.begin(), vec.end());

  int prevG = -1;
  bool ok = false, isPositive = false;

  for (auto p : vec) {
    if (prevG != p.first) {
      isPositive = false;
    }
    prevG = p.first;
    ok |= isPositive;
    isPositive |= isSame(p.second);
  }

  if (ok) {
    puts("-1");
    return 0;
  }

  vector<int> dp(N, 0), score(N, 0);

  for (int i = 0; i < N; i++) {
    if (isSame(i)) {
      score[scc[i]] = 1;
      dp[scc[i]] = 1;
    }
  }

  int ans = 0;

  for (auto v : vec) {
    int i = v.first;
    for (short s : scc.G[v.second]) {
      int to = scc[s];
      if (to == i) continue;
      dp[to] = max(dp[to], dp[i] + score[to]);
      ans = max(ans, dp[to]);
      debug(i, to, score[to]);
    }
  }

  cout << ans << endl;

  return 0;
}
accuracy: 0.25
time_ms: 480
memory_kb: 173248
final_rank: 18

code_snippet: #include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <algorithm>
#include <bitset>
#include <complex>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <cassert>
#include <functional>

typedef long long ll;
using namespace std;

#ifndef LOCAL
#define debug(...) ;
#else
#define debug(...) cerr << __LINE__ << " : " << #__VA_ARGS__ << " = " << _tostr(__VA_ARGS__) << endl;

template<typename T>
ostream &operator<<(ostream &out, const vector<T> &v);

template<typename T1, typename T2>
ostream &operator<<(ostream &out, const pair<T1, T2> &p) {
  out << "{" << p.first << ", " << p.second << "}";
  return out;
}

template<typename T>
ostream &operator<<(ostream &out, const vector<T> &v) {
  out << '{';
  for (const T &item : v) out << item << ", ";
  out << "\b\b}";
  return out;
}

void _tostr_rec(ostringstream &oss) {
  oss << "\b\b \b";
}

template<typename Head, typename... Tail>
void _tostr_rec(ostringstream &oss, Head &&head, Tail &&... tail) {
  oss << head << ", ";
  _tostr_rec(oss, forward<Tail>(tail)...);
}

template<typename... T>
string _tostr(T &&... args) {
  ostringstream oss;
  int size = sizeof...(args);
  if (size > 1) oss << "{";
  _tostr_rec(oss, forward<T>(args)...);
  if (size > 1) oss << "}";
  return oss.str();
}
#endif

#define mod 1000000007 //1e9+7(prime number)
#define INF 1000000000 //1e9
#define LLINF 2000000000000000000LL //2e18
#define SIZE 110

/* Strongly Connected Component */

/*
 1. dfsをして、戻るときに1から順に番号を付ける
 2. 数値が大きいノードから逆辺を使ってdfsをする。
    すでに訪れているノードには行かない。
    たどり着けるノードが同じ連結成分に属する。
 */

struct SCC {
  int n;
  vector<vector<short>> G, rG;
  vector<int> vs, cmp;
  vector<bool> used;

  SCC(int n): n(n), G(n), rG(n), cmp(n) {}

  void add_edge(short from, short to) {
    G[from].push_back(to);
    rG[to].push_back(from);
  }

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

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

  int solve() {
    used.assign(n, false);
    vs.clear();
    for (int i = 0; i < n; i++)
      if (!used[i]) dfs(i);
    used.assign(n, false);
    int k = 0;
    for (int i = (int)vs.size() - 1; i >= 0; i--)
      if (!used[vs[i]]) rdfs(vs[i], k++);
    return k; //強連結成分数
  }

  //属する強連結成分番号(トポロジカル順)
  int operator[](int k) const {
    return cmp[k];
  }
};

struct User {
  int N, M;
  char C[SIZE];
  vector<int> G[SIZE];

  void scan() {
    scanf("%d%d", &N, &M);
    for (int i = 0; i < N; i++) {
      scanf(" %c", C + i);
    }
    for (int i = 0; i < M; i++) {
      int a, b;
      scanf("%d%d", &a, &b);
      G[a - 1].push_back(b - 1);
    }
  }
};

int main() {
  User A, B;

  A.scan();
  B.scan();

  SCC scc(A.N * B.N);

  auto isSame = [&](int x) {
    return A.C[x / B.N] == B.C[x % B.N];
  };

  for (int i = 0; i < A.N; i++) {
    for (int j = 0; j < B.N; j++) {
      int from = i * B.N + j;

      for (int a : A.G[i]) {
        for (int b : B.G[j]) {
          int to = a * B.N + b;
          scc.add_edge(from, to);
        }
      }
      if (!isSame(from)) {
        for (int a : A.G[i]) {
          scc.add_edge(from, a * B.N + j);
        }
        for (int b : B.G[j]) {
          scc.add_edge(from, i * B.N + b);
        }
      }
    }
  }

  scc.solve();

  vector<pair<int, int>> vec;
  int N = A.N * B.N;

  for (int i = 0; i < N; i++) {
    vec.push_back({scc[i], i});
  }

  sort(vec.begin(), vec.end());

  int prevG = -1;
  bool ok = false, isPositive = false;

  for (auto p : vec) {
    if (prevG != p.first) {
      isPositive = false;
    }
    prevG = p.first;
    ok |= isPositive;
    isPositive |= isSame(p.second);
  }

  if (ok) {
    puts("-1");
    return 0;
  }

  vector<int> dp(N, 0), score(N, 0);

  for (int i = 0; i < N; i++) {
    if (isSame(i)) {
      score[scc[i]] = 1;
      dp[scc[i]] = 1;
    }
  }

  int ans = 0;

  for (auto v : vec) {
    int i = v.first;
    for (int s : scc.G[v.second]) {
      int to = scc[s];
      if (to == i) continue;
      dp[to] = max(dp[to], dp[i] + score[to]);
      ans = max(ans, dp[to]);
      debug(i, to, score[to]);
    }
  }

  cout << ans << endl;

  return 0;
}
accuracy: 0.25
time_ms: 470
memory_kb: 173264
final_rank: 17

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


enum Type{
	A,
	B,
};

struct Info{
	Info(){

		loc[A] = loc[B] = value = 0;
	}
	Info(int loc_A,int loc_B,int arg_value){
		loc[A] = loc_A;
		loc[B] = loc_B;
		value = arg_value;
	}
	void set(int loc_A,int loc_B,int arg_value){
		loc[A] = loc_A;
		loc[B] = loc_B;
		value = arg_value;
	}
	bool operator<(const struct Info &arg) const{

		return value < arg.value; //valueの昇順(PQ)
	}

	int loc[2],value;
};

int N[2],M[2],in_num[2][105];
char CH[2][105];
vector<int> G[2][105],scc_G[2][105];

struct GROUP{
	vector<int> nodes;
};


#define NUM 105

GROUP group[2][NUM]; //強連結成分のグループ
vector<int> reverse_G[2][NUM];
stack<int> S;

bool check[NUM],can_add[NUM][NUM];
int ans;
int table[NUM],dp[105][105];
double sleep[NUM];

int group_index;

void dfs(Type type,int node_id){
	check[node_id] = true;

	for(int i = 0; i < G[type][node_id].size(); i++){
		if(!check[G[type][node_id][i]])dfs(type,G[type][node_id][i]);
	}
	S.push(node_id);
}

void reverse_dfs(Type type,int node_id){
	check[node_id] = true;

	group[type][group_index].nodes.push_back(node_id); //group[group_index]にノードを突っ込む
	table[node_id] = group_index;

	for(int i = 0; i < reverse_G[type][node_id].size(); i++){
		if(!check[reverse_G[type][node_id][i]])reverse_dfs(type,reverse_G[type][node_id][i]);
	}
}


int func(int node_A,int node_B){


	if(dp[node_A][node_B] != -1){

		return dp[node_A][node_B];
	}

	dp[node_A][node_B] = 0;
	if(can_add[node_A][node_B]){

		dp[node_A][node_B] = 1;
	}

	int tmp = 0;

	//AとBが両方動く
	for(int i = 0; i < scc_G[A][node_A].size(); i++){
		int next_A = scc_G[A][node_A][i];
		for(int k = 0; k < scc_G[B][node_B].size(); k++){
			int next_B = scc_G[B][node_B][k];
			tmp = max(tmp,func(next_A,next_B));
		}
	}


	//Aだけが動く(Bがとどまっていられる場合)
	if(!can_add[node_A][node_B] || (group[B][node_B].nodes.size() > 1)){

		for(int i = 0; i < scc_G[A][node_A].size(); i++){
			int next_A = scc_G[A][node_A][i];
			int next_B = node_B;

			tmp = max(tmp,func(next_A,next_B));
		}
	}

	//Bだけが動く(Aがとどまっていられる場合)
	if(!can_add[node_A][node_B] || (group[A][node_A].nodes.size() > 1)){

		int next_A = node_A;
		for(int k = 0; k < scc_G[B][node_B].size(); k++){
			int next_B = scc_G[B][node_B][k];

			tmp = max(tmp,func(next_A,next_B));
		}
	}

 	 dp[node_A][node_B] += tmp;
 	 ans = max(ans,dp[node_A][node_B]);

	return dp[node_A][node_B];
}


int main(){

	scanf("%d %d",&N[A],&M[A]);

	char buf[2];

	for(int i = 0; i < N[A]; i++){

		in_num[A][i] = 0;
	}

	for(int i = 0; i < N[A]; i++){

		scanf("%s",buf);
		CH[A][i] = buf[0];
	}

	int from,to;
	for(int i = 0; i < M[A]; i++){

		scanf("%d %d",&from,&to);
		from--;
		to--;
		G[A][from].push_back(to);
		reverse_G[A][to].push_back(from);

	}

	scanf("%d %d",&N[B],&M[B]);

	for(int i = 0; i < N[B]; i++){

		in_num[B][i] = 0;
	}

	for(int i = 0; i < N[B]; i++){

		scanf("%s",buf);
		CH[B][i] = buf[0];
	}

	for(int i = 0; i < M[B]; i++){

		scanf("%d %d",&from,&to);
		from--;
		to--;
		G[B][from].push_back(to);
		reverse_G[B][to].push_back(from);
	}

	for(int i = 0; i < N[A]; i++)check[i] = false;

	//まずは帰りがけ順を計算
	for(int i = 0; i < N[A];i++){
		if(!check[i])dfs(A,i);
	}

	for(int i = 0; i < N[A];i++)check[i] = false;

	group_index = -1;
	//ノードを、帰りがけ順の逆順に、各強連結成分(グループ)に分解
	while(!S.empty()){
		if(!check[S.top()]){
			group_index++;

			reverse_dfs(A,S.top());
		}
		S.pop();
	}

	int num_A = group_index;

	//SCC分解した状態で、DAGにおける入次数を計算する
	for(int i = 0; i <= num_A; i++)in_num[A][i] = 0;

	for(int i = 0; i <= num_A; i++){
		for(int k = 0; k < group[A][i].nodes.size(); k++){
			for(int p = 0; p < G[A][group[A][i].nodes[k]].size(); p++){
				int next_group = table[G[A][group[A][i].nodes[k]][p]];
				if(next_group != i){
					in_num[A][next_group]++;
					scc_G[A][i].push_back(next_group);
				}
			}
		}
	}
	for(int i = 0; i <= num_A; i++){
		if(scc_G[A][i].size() == 0)continue;

		sort(scc_G[A][i].begin(),scc_G[A][i].end());
		scc_G[A][i].erase(unique(scc_G[A][i].begin(),scc_G[A][i].end()),scc_G[A][i].end());
	}


	for(int i = 0; i < N[B]; i++)check[i] = false;

	//まずは帰りがけ順を計算
	for(int i = 0; i < N[B];i++){
		if(!check[i])dfs(B,i);
	}

	for(int i = 0; i < N[B];i++)check[i] = false;

	group_index = -1;
	//ノードを、帰りがけ順の逆順に、各強連結成分(グループ)に分解
	while(!S.empty()){
		if(!check[S.top()]){
			group_index++;

			reverse_dfs(B,S.top());
		}
		S.pop();
	}

	int num_B = group_index;

	for(int i = 0; i <= num_A; i++){
		for(int k = 0; k <= num_B; k++){

			can_add[i][k] = false;
			dp[i][k] = -1;
		}
	}

	//無限かチェック
	for(int i = 0; i <= num_A; i++){
		for(int k = 0; k <= num_B; k++){
			bool FLG = false;

			for(int a = 0; a < group[A][i].nodes.size(); a++){
				for(int b = 0; b < group[B][k].nodes.size(); b++){
					//無限に増える
					if(CH[A][group[A][i].nodes[a]] == CH[B][group[B][k].nodes[b]]){
						FLG = true;
						break;
					}
				}
				if(FLG)break;
			}
			if(FLG){
				if(group[A][i].nodes.size() > 1 && group[B][k].nodes.size() > 1){

					printf("-1\n");
					return 0;
				}
				can_add[i][k] = true; //増える
			}
		}
	}

	//SCC分解した状態で、DAGにおける入次数を計算する
	for(int i = 0; i <= num_B; i++)in_num[B][i] = 0;

	for(int i = 0; i <= num_B; i++){
		for(int k = 0; k < group[B][i].nodes.size(); k++){
			for(int p = 0; p < G[B][group[B][i].nodes[k]].size(); p++){
				int next_group = table[G[B][group[B][i].nodes[k]][p]];
				if(next_group != i){
					in_num[B][next_group]++;
					scc_G[B][i].push_back(next_group);
				}
			}
		}
	}
	for(int i = 0; i <= num_B; i++){
		if(scc_G[B][i].size() == 0)continue;

		sort(scc_G[B][i].begin(),scc_G[B][i].end());
		scc_G[B][i].erase(unique(scc_G[B][i].begin(),scc_G[B][i].end()),scc_G[B][i].end());
	}

	vector<int> vec_A,vec_B;

	//入次数0の連結成分をpush
	for(int i = 0; i <= num_A; i++){
		if(in_num[A][i] == 0){

			vec_A.push_back(i);
		}
	}
	for(int i = 0; i <= num_B; i++){
		if(in_num[B][i] == 0){

			vec_B.push_back(i);
		}
	}

	ans = 0;

	for(int i = 0; i < vec_A.size(); i++){
		for(int k = 0; k < vec_B.size(); k++){
			func(vec_A[i],vec_B[k]);
		}
	}

	printf("%d\n",ans);

	return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3488
final_rank: 1

code_snippet: #include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <utility>
#include <tuple>
using namespace std;

using Graph = vector< vector<int> >;

void read_field(vector<char> &board, Graph &G) {
  int N, M; cin >> N >> M;
  board.resize(N);
  G.resize(N);

  for(int i=0; i<N; i++) {
    cin >> board[i];
  }

  for(int i=0; i<M; i++) {
    int u, v; cin >> u >> v;
    u--; v--;

    // directed !!!
    G[u].push_back(v);
  }
}

int rec[110][110];
struct Elem {
  int u1, u2, cost;
};

int main() {
  vector<char> A_char, B_char;
  Graph A_Graph, B_Graph;
  read_field(A_char, A_Graph);
  read_field(B_char, B_Graph);

  bool operate_inf = false;
  int N1 = A_char.size(), N2 = B_char.size();

  queue< Elem > que;

  // equal
  for(int i=0; i<N1; i++) {
    for(int j=0; j<N2; j++) {
      if(A_char[i] == B_char[j]) {
        rec[i][j] = 1;
        que.push(Elem{i, j, 1});
      }
    }
  }

  while(que.size()) {
    int u1, u2, p_cost;

    u1 = que.front().u1;
    u2 = que.front().u2;
    p_cost = que.front().cost;
    que.pop();
    
    if(p_cost < rec[u1][u2]) continue;
    if(rec[u1][u2] > max(N1, N2)) {
      operate_inf = true;
      break;
    }
    else {
      // move (both)
      for(auto v1 : A_Graph[u1]) {
        for(auto v2 : B_Graph[u2]) {
          char c1 = A_char[v1], c2 = B_char[v2];

          int cost = 0;
          if(c1 == c2) {
            cost = p_cost + 1;
          }
          else {
            cost = p_cost;
          }

          if(rec[v1][v2] < cost) {
            rec[v1][v2] = cost;
            que.push(Elem{v1, v2, cost});
          }
        }
      }

      if(A_char[u1] != B_char[u2]) {  
        // move (A)
        for(auto v1 : A_Graph[u1]) {
          int v2 = u2;

          char c1 = A_char[v1], c2 = B_char[v2];

          int cost = 0;
          if(c1 == c2) {
            cost = p_cost + 1;
          }
          else {
            cost = p_cost;
          }

          if(rec[v1][v2] < cost) {
            rec[v1][v2] = cost;
            que.push(Elem{v1, v2, cost});
          }
        }

        // move (B)
        for(auto v2 : B_Graph[u2]) {
          int v1 = u1;

          char c1 = A_char[v1], c2 = B_char[v2];

          int cost = 0;
          if(c1 == c2) {
            cost = p_cost + 1;
          }
          else {
            cost = p_cost;
          }

          if(rec[v1][v2] < cost) {
            rec[v1][v2] = cost;
            que.push(Elem{v1, v2, cost});
          }
        }
      }
    }
  }

  if(operate_inf) {
    cout << -1 << endl;
    return 0;
  }
  
  int ans = 0;
  for(int i=0; i<N1; i++) {
    for(int j=0; j<N2; j++) {
      // printf("%d ", rec[i][j]);
      ans = max(ans, rec[i][j]);
    }
    // cout << endl;
  }
  cout << ans << endl;
  return 0;
}
accuracy: 1
time_ms: 260
memory_kb: 3340
final_rank: 5

code_snippet: #include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <utility>
#include <tuple>
using namespace std;

using Graph = vector< vector<int> >;

void read_field(vector<char> &board, Graph &G) {
  int N, M; cin >> N >> M;
  board.resize(N);
  G.resize(N);

  for(int i=0; i<N; i++) {
    cin >> board[i];
  }

  for(int i=0; i<M; i++) {
    int u, v; cin >> u >> v;
    u--; v--;

    // directed !!!
    G[u].push_back(v);
  }
}

int rec[110][110];
struct Elem {
  int u1, u2, cost;
};

int main() {
  vector<char> A_char, B_char;
  Graph A_Graph, B_Graph;
  read_field(A_char, A_Graph);
  read_field(B_char, B_Graph);

  bool operate_inf = false;
  int N1 = A_char.size(), N2 = B_char.size();

  queue< Elem > que;

  // equal
  for(int i=0; i<N1; i++) {
    for(int j=0; j<N2; j++) {
      if(A_char[i] == B_char[j]) {
        rec[i][j] = 1;
        que.push(Elem{i, j, 1});
      }
    }
  }

  while(que.size()) {
    int u1, u2, p_cost;

    u1 = que.front().u1;
    u2 = que.front().u2;
    p_cost = que.front().cost;
    que.pop();
    
    if(p_cost < rec[u1][u2]) continue;
    if(rec[u1][u2] > N1 + N2) {
      operate_inf = true;
      break;
    }
    else {
      // move (both)
      for(auto v1 : A_Graph[u1]) {
        for(auto v2 : B_Graph[u2]) {
          char c1 = A_char[v1], c2 = B_char[v2];

          int cost = 0;
          if(c1 == c2) {
            cost = p_cost + 1;
          }
          else {
            cost = p_cost;
          }

          if(rec[v1][v2] < cost) {
            rec[v1][v2] = cost;
            que.push(Elem{v1, v2, cost});
          }
        }
      }

      if(A_char[u1] != B_char[u2]) {  
        // move (A)
        for(auto v1 : A_Graph[u1]) {
          int v2 = u2;

          char c1 = A_char[v1], c2 = B_char[v2];

          int cost = 0;
          if(c1 == c2) {
            cost = p_cost + 1;
          }
          else {
            cost = p_cost;
          }

          if(rec[v1][v2] < cost) {
            rec[v1][v2] = cost;
            que.push(Elem{v1, v2, cost});
          }
        }

        // move (B)
        for(auto v2 : B_Graph[u2]) {
          int v1 = u1;

          char c1 = A_char[v1], c2 = B_char[v2];

          int cost = 0;
          if(c1 == c2) {
            cost = p_cost + 1;
          }
          else {
            cost = p_cost;
          }

          if(rec[v1][v2] < cost) {
            rec[v1][v2] = cost;
            que.push(Elem{v1, v2, cost});
          }
        }
      }
    }
  }

  if(operate_inf) {
    cout << -1 << endl;
    return 0;
  }
  
  int ans = 0;
  for(int i=0; i<N1; i++) {
    for(int j=0; j<N2; j++) {
      // printf("%d ", rec[i][j]);
      ans = max(ans, rec[i][j]);
    }
    // cout << endl;
  }
  cout << ans << endl;
  return 0;
}
accuracy: 1
time_ms: 620
memory_kb: 3308
final_rank: 6

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

int N[2], M[2];
char c[2][100];
vector< vector< int > > edge[2];
bool retable[2][100], vis[100];

bool dfs(int v, int s, int i) {
	if (vis[v]) return v == s ? true : false;
	vis[v] = true;
	for (int u : edge[i][v]) if (dfs(u, s, i)) return true;
	return false;
}

void readGraph(int k) {
	cin >> N[k] >> M[k];
	for (int i=0; i<N[k]; ++i) cin >> c[k][i];
	
	edge[k].assign(N[k], vector< int >());
	for (int i=0; i<M[k]; ++i) {
		int a, b;
		cin >> a >> b;
		--a; --b;
		edge[k][a].push_back(b);
	}
	
	memset(retable[k], 0, sizeof(retable[k]));
	for (int v=0; v<N[k]; ++v) {
		memset(vis, 0, sizeof(vis));
		retable[k][v] = dfs(v, v, k);
	}
}

int dp[101][101];
struct State { int u, v, score; };

bool sameAlph(int u, int v) { return c[0][u] == c[1][v]; }

int main() {
	readGraph(0);
	readGraph(1);
		
	for (int u=0; u<N[0]; ++u) for (int v=0; v<N[1]; ++v) {
		if (retable[0][u] && retable[1][v] && c[0][u] == c[1][v]) {
			puts("-1");
			return 0;
		}
	}
	
	memset(dp, -1, sizeof(dp));
	queue< State > que;
	for (int u=0; u<N[0]; ++u) for (int v=0; v<N[1]; ++v) {
		int score = sameAlph(u, v) ? 1 : 0;
		que.push(State{u, v, score});
		dp[u][v] = score;
	}
	
	while (!que.empty()) {
		State s = que.front(); que.pop();		
		if (dp[s.u][s.v] > s.score) continue;
		
		for (int nu : edge[0][s.u]) for (int nv : edge[1][s.v]) {
			int nxt_score = s.score + (sameAlph(nu, nv) ? 1 : 0);
			if (dp[nu][nv] < nxt_score) {
				dp[nu][nv] = nxt_score;
				que.push(State{nu, nv, nxt_score});
			}
		}
		
		if (!sameAlph(s.u, s.v)) {
			for (int nu : edge[0][s.u]) {
				int nv = s.v;
				int nxt_score = s.score + (sameAlph(nu, nv) ? 1 : 0);
				if (dp[nu][nv] < nxt_score) {
					dp[nu][nv] = nxt_score;
					que.push(State{nu, nv, nxt_score});
				}
			}
			
			for (int nv : edge[1][s.v]) {
				int nu = s.u;
				int nxt_score = s.score + (sameAlph(nu, nv) ? 1 : 0);
				if (dp[nu][nv] < nxt_score) {
					dp[nu][nv] = nxt_score;
					que.push(State{nu, nv, nxt_score});
				}
			}
		}
	}
	
	int ans = 0;
	for (int u=0; u<N[0]; ++u) for (int v=0; v<N[1]; ++v) ans = max(ans, dp[u][v]);
	cout << ans << endl;
}
accuracy: 1
time_ms: 250
memory_kb: 3304
final_rank: 4

code_snippet: #include<stdio.h>
#include <iostream>
#include <math.h>
#include <numeric>
#include <vector>
#include <map>
#include <functional>
#include <stdio.h>
#include <array>
#include <algorithm>
#include <string>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <queue>
#include<iomanip>
#include<bitset>
#include<stack>
#include<set>
#include<limits>
#include <complex>
using namespace std;

const int mod = 1000000007;
struct Mod {
public:
	int num;
	Mod() : num(0) { ; }
	Mod(long long int n) : num((n % mod + mod) % mod) { ; }
	Mod(int n) : num((n % mod + mod) % mod) { ; }
	operator int() { return num; }
};

Mod operator+(const Mod a, const Mod b) { return Mod((a.num + b.num) % mod); }
Mod operator+(const long long int a, const Mod b) { return Mod(a) + b; }
Mod operator++(Mod &a) { return a + Mod(1); }
Mod operator-(const Mod a, const Mod b) { return Mod((mod + a.num - b.num) % mod); }
Mod operator-(const long long int a, const Mod b) { return Mod(a) - b; }
Mod operator--(Mod &a) { return a - Mod(1); }
Mod operator*(const Mod a, const Mod b) { return Mod(((long long)a.num * b.num) % mod); }
Mod operator*(const long long int a, const Mod b) { return Mod(a)*b; }
Mod operator+=(Mod &a, const Mod b) { return a = a + b; }
Mod operator+=(long long int &a, const Mod b) { return a = a + b; }
Mod operator-=(Mod &a, const Mod b) { return a = a - b; }
Mod operator-=(long long int &a, const Mod b) { return a = a - b; }
Mod operator*=(Mod &a, const Mod b) { return a = a * b; }
Mod operator*=(long long int &a, const Mod b) { return a = a * b; }
Mod operator^(const Mod a, const int n) {
	if (n == 0) return Mod(1);
	Mod res = (a * a) ^ (n / 2);
	if (n % 2) res = res * a;
	return res;
}
Mod inv(const Mod a) { return a ^ (mod - 2); }
Mod operator/(const Mod a, const Mod b) {
	assert(b.num != 0);
	return a * inv(b);
}
Mod operator/(const long long int a, const Mod b) {
	assert(b.num != 0);
	return Mod(a) * inv(b);
}
Mod operator/=(Mod &a, const Mod b) {
	assert(b.num != 0);
	return a = a * inv(b);
}

#define MAX_N 1024000

Mod fact[MAX_N], factinv[MAX_N];
void init() {
	fact[0] = Mod(1); factinv[0] = 1;
	for (int i = 0; i < MAX_N - 1; ++i) {
		fact[i + 1] = fact[i] * Mod(i + 1);
		factinv[i + 1] = factinv[i] / Mod(i + 1);
	}
}
Mod comb(const int a, const int b) {
	return fact[a] * factinv[b] * factinv[a - b];
}
template<typename T>
vector<vector<T>> keisann(const vector<vector<T>>l, const vector<vector<T>>r) {
	vector<vector<T>>ans(l.size(), vector<T>(r[0].size()));
	assert(l[0].size() == r.size());
	for (unsigned int h = 0; h < l.size(); ++h) {
		for (unsigned int i = 0; i < r.size(); ++i) {
			for (unsigned int w = 0; w < r[0].size(); ++w) {

				ans[h][w] += l[h][i] * r[i][w];
			}
		}
	}
	return ans;
}
template<typename T>
vector<vector<T>>powgyou(vector<vector<T>>a, const long long int n) {
	assert(a.size() == a[0].size());
	if (!n) {
		vector<vector<T>>e(a.size(), vector<T>(a[0].size()));
		for (unsigned int i = 0; i < a.size(); ++i) {
			e[i][i] = 1;
		}
		return e;
	}
	if (n == 1)return a;
	else {
		vector<vector<T>>ans(a.size(), vector<T>(a[0].size(), 0));
		ans = powgyou(a, n / 2);
		ans = keisann(ans, ans);
		if (n % 2) {
			ans = keisann(ans, a);
		}
		return ans;
	}
}

long long int powint(long long int a, int b) {
	if (b == 0)return 1;
	if (b == 1)return a;
	else {
		long long int ans = 1;
		long long int c = powint(a, b / 2);
		ans *= c*c;
		ans *= (b % 2) ? a : 1;
		return ans;
	}

}

struct Used {
	int ap;
	int bp;
	int point;
};


vector<vector<int>>aedges(100);
vector<vector<int>>bedges(100);
vector<int>anums;
vector<int>bnums;

int memo[100][100];

int getans(int ap, int bp, vector<Used>&used, int nowp) {
	bool gp = false;
	if (anums[ap] == bnums[bp]) {
		nowp++;
		gp = true;
	}
	if (memo[ap][bp] >= 0)return memo[ap][bp];
	auto it = find_if(used.begin(), used.end(), [=](const Used& l) {return l.ap == ap&&l.bp == bp; });
	if (it != used.end()) {
		if (it->point < nowp)return memo[ap][bp] = 99999999;
		else {
			return nowp;
		}
	}
	int ans = 0;
	for (int i = 0; i <= aedges[ap].size(); ++i) {
		for (int j = 0; j <= bedges[bp].size(); ++j) {
			if (gp && ((i == aedges[ap].size()) || (j == bedges[bp].size())))continue;
			if (i == aedges[ap].size() && j == bedges[bp].size())continue;
			int naedge = (i == aedges[ap].size()) ? ap : aedges[ap][i];
			int nbedge = (j == bedges[bp].size()) ? bp : bedges[bp][j];

			used.push_back(Used{ ap, bp, nowp });
			ans = max(ans, getans(naedge, nbedge, used, nowp));
			used.pop_back();
		}
	}
	return memo[ap][bp] = ans + (anums[ap] == bnums[bp]);
}


int main(void) {
	memset(memo, -1, sizeof(memo));
	int aN, aM; cin >> aN >> aM;

	for (int i = 0; i < aN; ++i) {
		char c; cin >> c;
		anums.push_back(c - 'a');
	}
	for (int i = 0; i < aM; ++i) {
		int a, b; cin >> a >> b;
		a--; b--;
		aedges[a].push_back(b);
	}


	int bN, bM; cin >> bN >> bM;

	for (int i = 0; i < bN; ++i) {
		char c; cin >> c;
		bnums.push_back(c - 'a');
	}
	for (int i = 0; i < bM; ++i) {
		int a, b; cin >> a >> b;
		a--; b--;
		bedges[a].push_back(b);
	}
	int ans = 0;
	for (int i = 0; i < aN; ++i) {
		for (int j = 0; j < bN; ++j) {
			vector<Used> a;
			ans = max(ans, getans(i, j, a, 0));
		}
	}
	if (ans >= 99999999)cout << -1 << endl;
	else {
		cout << ans << endl;
	}
	return 0;
}

/*
4
0 4
7 12
6 8
4 6

*/
accuracy: 0.7142857142857143
time_ms: 320
memory_kb: 12728
final_rank: 15

code_snippet: #include<stdio.h>
#include <iostream>
#include <math.h>
#include <numeric>
#include <vector>
#include <map>
#include <functional>
#include <stdio.h>
#include <array>
#include <algorithm>
#include <string>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <queue>
#include<iomanip>
#include<bitset>
#include<stack>
#include<set>
#include<limits>
#include <complex>
using namespace std;

const int mod = 1000000007;
struct Mod {
public:
	int num;
	Mod() : num(0) { ; }
	Mod(long long int n) : num((n % mod + mod) % mod) { ; }
	Mod(int n) : num((n % mod + mod) % mod) { ; }
	operator int() { return num; }
};

Mod operator+(const Mod a, const Mod b) { return Mod((a.num + b.num) % mod); }
Mod operator+(const long long int a, const Mod b) { return Mod(a) + b; }
Mod operator++(Mod &a) { return a + Mod(1); }
Mod operator-(const Mod a, const Mod b) { return Mod((mod + a.num - b.num) % mod); }
Mod operator-(const long long int a, const Mod b) { return Mod(a) - b; }
Mod operator--(Mod &a) { return a - Mod(1); }
Mod operator*(const Mod a, const Mod b) { return Mod(((long long)a.num * b.num) % mod); }
Mod operator*(const long long int a, const Mod b) { return Mod(a)*b; }
Mod operator+=(Mod &a, const Mod b) { return a = a + b; }
Mod operator+=(long long int &a, const Mod b) { return a = a + b; }
Mod operator-=(Mod &a, const Mod b) { return a = a - b; }
Mod operator-=(long long int &a, const Mod b) { return a = a - b; }
Mod operator*=(Mod &a, const Mod b) { return a = a * b; }
Mod operator*=(long long int &a, const Mod b) { return a = a * b; }
Mod operator^(const Mod a, const int n) {
	if (n == 0) return Mod(1);
	Mod res = (a * a) ^ (n / 2);
	if (n % 2) res = res * a;
	return res;
}
Mod inv(const Mod a) { return a ^ (mod - 2); }
Mod operator/(const Mod a, const Mod b) {
	assert(b.num != 0);
	return a * inv(b);
}
Mod operator/(const long long int a, const Mod b) {
	assert(b.num != 0);
	return Mod(a) * inv(b);
}
Mod operator/=(Mod &a, const Mod b) {
	assert(b.num != 0);
	return a = a * inv(b);
}

#define MAX_N 1024000

Mod fact[MAX_N], factinv[MAX_N];
void init() {
	fact[0] = Mod(1); factinv[0] = 1;
	for (int i = 0; i < MAX_N - 1; ++i) {
		fact[i + 1] = fact[i] * Mod(i + 1);
		factinv[i + 1] = factinv[i] / Mod(i + 1);
	}
}
Mod comb(const int a, const int b) {
	return fact[a] * factinv[b] * factinv[a - b];
}
template<typename T>
vector<vector<T>> keisann(const vector<vector<T>>l, const vector<vector<T>>r) {
	vector<vector<T>>ans(l.size(), vector<T>(r[0].size()));
	assert(l[0].size() == r.size());
	for (unsigned int h = 0; h < l.size(); ++h) {
		for (unsigned int i = 0; i < r.size(); ++i) {
			for (unsigned int w = 0; w < r[0].size(); ++w) {

				ans[h][w] += l[h][i] * r[i][w];
			}
		}
	}
	return ans;
}
template<typename T>
vector<vector<T>>powgyou(vector<vector<T>>a, const long long int n) {
	assert(a.size() == a[0].size());
	if (!n) {
		vector<vector<T>>e(a.size(), vector<T>(a[0].size()));
		for (unsigned int i = 0; i < a.size(); ++i) {
			e[i][i] = 1;
		}
		return e;
	}
	if (n == 1)return a;
	else {
		vector<vector<T>>ans(a.size(), vector<T>(a[0].size(), 0));
		ans = powgyou(a, n / 2);
		ans = keisann(ans, ans);
		if (n % 2) {
			ans = keisann(ans, a);
		}
		return ans;
	}
}

long long int powint(long long int a, int b) {
	if (b == 0)return 1;
	if (b == 1)return a;
	else {
		long long int ans = 1;
		long long int c = powint(a, b / 2);
		ans *= c*c;
		ans *= (b % 2) ? a : 1;
		return ans;
	}

}

struct Used {
	int ap;
	int bp;
	int point;
};


vector<vector<int>>aedges(100);
vector<vector<int>>bedges(100);
vector<int>anums;
vector<int>bnums;

int memo[100][100];

int getans(int ap, int bp, vector<Used>&used, int nowp) {
	bool gp = false;
	if (anums[ap] == bnums[bp]) {
		nowp++;
		gp = true;
	}
	if (memo[ap][bp] >= 0)return memo[ap][bp];
	if (nowp > 100)return 99999999;
	int ans = 0;
	for (int i = 0; i <= aedges[ap].size(); ++i) {
		for (int j = 0; j <= bedges[bp].size(); ++j) {
			if (gp && ((i == aedges[ap].size()) || (j == bedges[bp].size())))continue;
			if (i == aedges[ap].size() && j == bedges[bp].size())continue;
			int naedge = (i == aedges[ap].size()) ? ap : aedges[ap][i];
			int nbedge = (j == bedges[bp].size()) ? bp : bedges[bp][j];

			used.push_back(Used{ ap, bp, nowp });
			ans = max(ans, getans(naedge, nbedge, used, nowp));
			used.pop_back();
		}
	}
	return memo[ap][bp] = ans + (anums[ap] == bnums[bp]);
}


int main(void) {
	memset(memo, -1, sizeof(memo));
	int aN, aM; cin >> aN >> aM;

	for (int i = 0; i < aN; ++i) {
		char c; cin >> c;
		anums.push_back(c - 'a');
	}
	for (int i = 0; i < aM; ++i) {
		int a, b; cin >> a >> b;
		a--; b--;
		aedges[a].push_back(b);
	}


	int bN, bM; cin >> bN >> bM;

	for (int i = 0; i < bN; ++i) {
		char c; cin >> c;
		bnums.push_back(c - 'a');
	}
	for (int i = 0; i < bM; ++i) {
		int a, b; cin >> a >> b;
		a--; b--;
		bedges[a].push_back(b);
	}
	int ans = 0;
	for (int i = 0; i < aN; ++i) {
		for (int j = 0; j < bN; ++j) {
			vector<Used> a;
			ans = max(ans, getans(i, j, a, 0));
		}
	}
	if (ans >= 99999999)cout << -1 << endl;
	else {
		cout << ans << endl;
	}
	return 0;
}

/*
4
0 4
7 12
6 8
4 6

*/
accuracy: 0.03571428571428571
time_ms: 10
memory_kb: 20056
final_rank: 20

code_snippet: #include<stdio.h>
#include <iostream>
#include <math.h>
#include <numeric>
#include <vector>
#include <map>
#include <functional>
#include <stdio.h>
#include <array>
#include <algorithm>
#include <string>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <queue>
#include<iomanip>
#include<bitset>
#include<stack>
#include<set>
#include<limits>
#include <complex>
using namespace std;

const int mod = 1000000007;
struct Mod {
public:
	int num;
	Mod() : num(0) { ; }
	Mod(long long int n) : num((n % mod + mod) % mod) { ; }
	Mod(int n) : num((n % mod + mod) % mod) { ; }
	operator int() { return num; }
};

Mod operator+(const Mod a, const Mod b) { return Mod((a.num + b.num) % mod); }
Mod operator+(const long long int a, const Mod b) { return Mod(a) + b; }
Mod operator++(Mod &a) { return a + Mod(1); }
Mod operator-(const Mod a, const Mod b) { return Mod((mod + a.num - b.num) % mod); }
Mod operator-(const long long int a, const Mod b) { return Mod(a) - b; }
Mod operator--(Mod &a) { return a - Mod(1); }
Mod operator*(const Mod a, const Mod b) { return Mod(((long long)a.num * b.num) % mod); }
Mod operator*(const long long int a, const Mod b) { return Mod(a)*b; }
Mod operator+=(Mod &a, const Mod b) { return a = a + b; }
Mod operator+=(long long int &a, const Mod b) { return a = a + b; }
Mod operator-=(Mod &a, const Mod b) { return a = a - b; }
Mod operator-=(long long int &a, const Mod b) { return a = a - b; }
Mod operator*=(Mod &a, const Mod b) { return a = a * b; }
Mod operator*=(long long int &a, const Mod b) { return a = a * b; }
Mod operator^(const Mod a, const int n) {
	if (n == 0) return Mod(1);
	Mod res = (a * a) ^ (n / 2);
	if (n % 2) res = res * a;
	return res;
}
Mod inv(const Mod a) { return a ^ (mod - 2); }
Mod operator/(const Mod a, const Mod b) {
	assert(b.num != 0);
	return a * inv(b);
}
Mod operator/(const long long int a, const Mod b) {
	assert(b.num != 0);
	return Mod(a) * inv(b);
}
Mod operator/=(Mod &a, const Mod b) {
	assert(b.num != 0);
	return a = a * inv(b);
}

#define MAX_N 1024000

Mod fact[MAX_N], factinv[MAX_N];
void init() {
	fact[0] = Mod(1); factinv[0] = 1;
	for (int i = 0; i < MAX_N - 1; ++i) {
		fact[i + 1] = fact[i] * Mod(i + 1);
		factinv[i + 1] = factinv[i] / Mod(i + 1);
	}
}
Mod comb(const int a, const int b) {
	return fact[a] * factinv[b] * factinv[a - b];
}
template<typename T>
vector<vector<T>> keisann(const vector<vector<T>>l, const vector<vector<T>>r) {
	vector<vector<T>>ans(l.size(), vector<T>(r[0].size()));
	assert(l[0].size() == r.size());
	for (unsigned int h = 0; h < l.size(); ++h) {
		for (unsigned int i = 0; i < r.size(); ++i) {
			for (unsigned int w = 0; w < r[0].size(); ++w) {
			
				ans[h][w] +=l[h][i] * r[i][w];
			}
		}
	}
	return ans;
}
template<typename T>
vector<vector<T>>powgyou(vector<vector<T>>a, const long long int n) {
	assert(a.size() == a[0].size());
	if (!n) {
		vector<vector<T>>e(a.size(), vector<T>(a[0].size()));
		for (unsigned int i = 0; i < a.size(); ++i) {
			e[i][i] = 1;
		}
		return e;
	}
	if (n == 1)return a;
	else {
		vector<vector<T>>ans(a.size(), vector<T>(a[0].size(), 0));
		ans = powgyou(a, n / 2);
		ans = keisann(ans, ans);
		if (n % 2) {
			ans = keisann(ans, a);
		}
		return ans;
	}
}

long long int powint(long long int a, int b) {
	if (b == 0)return 1;
	if (b == 1)return a;
	else {
		long long int ans = 1;
		long long int c = powint(a, b / 2);
		ans *= c*c;
		ans *= (b % 2) ? a : 1;
		return ans;
	}
	
}

struct Used {
	int ap;
	int bp;
	int point;
};


vector<vector<int>>aedges(100);
vector<vector<int>>bedges(100);
vector<int>anums;
vector<int>bnums;

int memo[100][100];

int getans(int ap, int bp, vector<Used>&used, int nowp) {
	bool gp = false;
	if (anums[ap] == bnums[bp]) {
		nowp++;
		gp = true;
	}
	if (memo[ap][bp] >= 0)return memo[ap][bp];
	auto it = find_if(used.begin(), used.end(), [=](const Used& l) {return l.ap == ap&&l.bp == bp; });
	if (it != used.end()) {
		if (it->point < nowp)return memo[ap][bp] = 9999999;
		else {
			return 0;
		}
	}
	int ans = 0;
	for (int i = 0; i <= aedges[ap].size(); ++i) {
		for (int j = 0; j <= bedges[bp].size(); ++j) {
			if (gp && ((i == aedges[ap].size()) || (j == bedges[bp].size())))continue;
			if (i == aedges[ap].size() && j == bedges[bp].size())continue;
			int naedge = (i == aedges[ap].size()) ? ap : aedges[ap][i];
			int nbedge = (j == bedges[bp].size() )? bp : bedges[bp][j];

			used.push_back(Used{ ap, bp, nowp });
			ans = max(ans,getans(naedge, nbedge, used, nowp));
			used.pop_back();
		}
	}
	return memo[ap][bp] = ans+ (anums[ap] == bnums[bp]);
}


int main(void) {
	memset(memo, -1, sizeof(memo));
	int aN, aM; cin >> aN >> aM;

	for (int i = 0; i < aN; ++i) {
		char c; cin >> c;
		anums.push_back(c - 'a');
	}
	for (int i = 0; i < aM; ++i) {
		int a, b; cin >> a >> b;
		a--; b--;
		aedges[a].push_back(b);
	}


	int bN, bM; cin >> bN >> bM;

	for (int i = 0; i < bN; ++i) {
		char c; cin >> c;
		bnums.push_back(c - 'a');
	}
	for (int i = 0; i < bM; ++i) {
		int a, b; cin >> a >> b;
		a--; b--;
		bedges[a].push_back(b);
	}
	int ans = 0;
	for (int i = 0; i < aN; ++i) {
		for (int j = 0; j < bN; ++j) {
			vector<Used> a;
			ans=max(ans,getans(i, j, a,0));
		}
	}
	if (ans >= 9999999)cout << -1 << endl;
	else {
		cout << ans << endl;
	}
	return 0;
}

/*
4
0 4
7 12
6 8
4 6

*/
accuracy: 0.7142857142857143
time_ms: 290
memory_kb: 12772
final_rank: 13

code_snippet: #include<stdio.h>
#include <iostream>
#include <math.h>
#include <numeric>
#include <vector>
#include <map>
#include <functional>
#include <stdio.h>
#include <array>
#include <algorithm>
#include <string>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <queue>
#include<iomanip>
#include<bitset>
#include<stack>
#include<set>
#include<limits>
#include <complex>
using namespace std;

const int mod = 1000000007;
struct Mod {
public:
	int num;
	Mod() : num(0) { ; }
	Mod(long long int n) : num((n % mod + mod) % mod) { ; }
	Mod(int n) : num((n % mod + mod) % mod) { ; }
	operator int() { return num; }
};

Mod operator+(const Mod a, const Mod b) { return Mod((a.num + b.num) % mod); }
Mod operator+(const long long int a, const Mod b) { return Mod(a) + b; }
Mod operator++(Mod &a) { return a + Mod(1); }
Mod operator-(const Mod a, const Mod b) { return Mod((mod + a.num - b.num) % mod); }
Mod operator-(const long long int a, const Mod b) { return Mod(a) - b; }
Mod operator--(Mod &a) { return a - Mod(1); }
Mod operator*(const Mod a, const Mod b) { return Mod(((long long)a.num * b.num) % mod); }
Mod operator*(const long long int a, const Mod b) { return Mod(a)*b; }
Mod operator+=(Mod &a, const Mod b) { return a = a + b; }
Mod operator+=(long long int &a, const Mod b) { return a = a + b; }
Mod operator-=(Mod &a, const Mod b) { return a = a - b; }
Mod operator-=(long long int &a, const Mod b) { return a = a - b; }
Mod operator*=(Mod &a, const Mod b) { return a = a * b; }
Mod operator*=(long long int &a, const Mod b) { return a = a * b; }
Mod operator^(const Mod a, const int n) {
	if (n == 0) return Mod(1);
	Mod res = (a * a) ^ (n / 2);
	if (n % 2) res = res * a;
	return res;
}
Mod inv(const Mod a) { return a ^ (mod - 2); }
Mod operator/(const Mod a, const Mod b) {
	assert(b.num != 0);
	return a * inv(b);
}
Mod operator/(const long long int a, const Mod b) {
	assert(b.num != 0);
	return Mod(a) * inv(b);
}
Mod operator/=(Mod &a, const Mod b) {
	assert(b.num != 0);
	return a = a * inv(b);
}

#define MAX_N 1024000

Mod fact[MAX_N], factinv[MAX_N];
void init() {
	fact[0] = Mod(1); factinv[0] = 1;
	for (int i = 0; i < MAX_N - 1; ++i) {
		fact[i + 1] = fact[i] * Mod(i + 1);
		factinv[i + 1] = factinv[i] / Mod(i + 1);
	}
}
Mod comb(const int a, const int b) {
	return fact[a] * factinv[b] * factinv[a - b];
}
template<typename T>
vector<vector<T>> keisann(const vector<vector<T>>l, const vector<vector<T>>r) {
	vector<vector<T>>ans(l.size(), vector<T>(r[0].size()));
	assert(l[0].size() == r.size());
	for (unsigned int h = 0; h < l.size(); ++h) {
		for (unsigned int i = 0; i < r.size(); ++i) {
			for (unsigned int w = 0; w < r[0].size(); ++w) {
			
				ans[h][w] +=l[h][i] * r[i][w];
			}
		}
	}
	return ans;
}
template<typename T>
vector<vector<T>>powgyou(vector<vector<T>>a, const long long int n) {
	assert(a.size() == a[0].size());
	if (!n) {
		vector<vector<T>>e(a.size(), vector<T>(a[0].size()));
		for (unsigned int i = 0; i < a.size(); ++i) {
			e[i][i] = 1;
		}
		return e;
	}
	if (n == 1)return a;
	else {
		vector<vector<T>>ans(a.size(), vector<T>(a[0].size(), 0));
		ans = powgyou(a, n / 2);
		ans = keisann(ans, ans);
		if (n % 2) {
			ans = keisann(ans, a);
		}
		return ans;
	}
}

long long int powint(long long int a, int b) {
	if (b == 0)return 1;
	if (b == 1)return a;
	else {
		long long int ans = 1;
		long long int c = powint(a, b / 2);
		ans *= c*c;
		ans *= (b % 2) ? a : 1;
		return ans;
	}
	
}

struct Used {
	int ap;
	int bp;
	int point;
};


vector<vector<int>>aedges(100);
vector<vector<int>>bedges(100);
vector<int>anums;
vector<int>bnums;

int memo[100][100];

int getans(int ap, int bp, vector<Used>&used, int nowp) {
	bool gp = false;
	if (anums[ap] == bnums[bp]) {
		nowp++;
		gp = true;
	}
	if (memo[ap][bp] >= 0)return memo[ap][bp];
	auto it = find_if(used.begin(), used.end(), [=](const Used& l) {return l.ap == ap&&l.bp == bp; });
	if (it != used.end()) {
		if (it->point < nowp)return memo[ap][bp] = 99999999;
		else {
			return nowp;
		}
	}
	int ans = 0;
	for (int i = 0; i <= aedges[ap].size(); ++i) {
		for (int j = 0; j <= bedges[bp].size(); ++j) {
			if (gp && ((i == aedges[ap].size()) || (j == bedges[bp].size())))continue;
			if (i == aedges[ap].size() && j == bedges[bp].size())continue;
			int naedge = (i == aedges[ap].size()) ? ap : aedges[ap][i];
			int nbedge = (j == bedges[bp].size() )? bp : bedges[bp][j];

			used.push_back(Used{ ap, bp, nowp });
			ans = max(ans,getans(naedge, nbedge, used, nowp));
			used.pop_back();
		}
	}
	return memo[ap][bp] = ans+ (anums[ap] == bnums[bp]);
}


int main(void) {
	memset(memo, -1, sizeof(memo));
	int aN, aM; cin >> aN >> aM;

	for (int i = 0; i < aN; ++i) {
		char c; cin >> c;
		anums.push_back(c - 'a');
	}
	for (int i = 0; i < aM; ++i) {
		int a, b; cin >> a >> b;
		a--; b--;
		aedges[a].push_back(b);
	}


	int bN, bM; cin >> bN >> bM;

	for (int i = 0; i < bN; ++i) {
		char c; cin >> c;
		bnums.push_back(c - 'a');
	}
	for (int i = 0; i < bM; ++i) {
		int a, b; cin >> a >> b;
		a--; b--;
		bedges[a].push_back(b);
	}
	int ans = 0;
	for (int i = 0; i < aN; ++i) {
		for (int j = 0; j < bN; ++j) {
			vector<Used> a;
			ans=max(ans,getans(i, j, a,0));
		}
	}
	if (ans >= 99999999)cout << -1 << endl;
	else {
		cout << ans << endl;
	}
	return 0;
}

/*
4
0 4
7 12
6 8
4 6

*/
accuracy: 0.7142857142857143
time_ms: 300
memory_kb: 12760
final_rank: 14

code_snippet: #include<iostream>
#include<vector>
#include<algorithm>
#include <iterator>
#include<queue>
#include <string>
#include <numeric>
#include<stack>
using namespace std;
#define rep(i,n) for(int i=0;i<(n);i++)
#define all(v) v.begin(),v.end()
vector< vector<int> > e[2];
struct Node{
	int id;
	int alpha;
	vector<int> ToCole;
};
struct To{
	int x1;
	int x2;
	int score;
	To(int a, int b, int c,int d ,int e){
		x1 = a; x2 = b;score = c;
	}
	To(int a, int b, int c){
		x1 = a; x2 = b; score = c;
	}
};
bool operator <(const To& T1, const To& T2){
	return T1.score < T2.score;
}
priority_queue < To > aStack;
vector<char> AlphaCole;
vector<vector<Node>> NodeColes(2);
vector<vector<int>> Tag(2,vector<int>(100));
int GetAns(vector<vector<int>>& memo, int p1, int p2){

	while (!aStack.empty()){
		p1 = aStack.top().x1;
		p2 = aStack.top().x2;
		int ascore = aStack.top().score;
		aStack.pop();
		if (ascore > 150){
			return -1;
		}
		if (Tag[0][p1] == Tag[1][p2]){
			for (auto i : e[0][p1]){
				for (auto j : e[1][p2]){
					if (Tag[0][i]==Tag[1][j]){
						if  (memo[i][j] <= ascore){
							aStack.push({ i, j, ascore+1 });
							memo[i][j] = ascore+1;
						}
					}
					else{
						if (memo[i][j] < ascore){
							aStack.push({ i, j, ascore });
							memo[i][j] = ascore;
						}
					}
				}
			}
		}
		else{
			for (auto i : e[0][p1]){
				for (auto j : e[1][p2]){
					if (Tag[0][i] == Tag[1][j]){
						if (memo[i][j] <= ascore){
							aStack.push({ i, j, ascore+1 });
							memo[i][j] = ascore + 1;
						}
					}
					else{
						if (memo[i][j] < ascore){
							aStack.push({ i, j, ascore });
							memo[i][j] = ascore;
						}
					}
				}
			}
			for (auto j : e[1][p2]){
				int i = p1;
				if (Tag[0][i] == Tag[1][j]){
					if (memo[i][j] <= ascore){
						aStack.push({ i, j, ascore+1 });
						memo[i][j] = ascore + 1;
					}
				}
				else{
					if (memo[i][j] < ascore){
						aStack.push({ i, j, ascore });
						memo[i][j] = ascore;
					}
				}
			}
			for (auto i : e[0][p1]){
				int j = p2;
				if (Tag[0][i] == Tag[1][j]){
					if (memo[i][j] <= ascore){
						aStack.push({ i, j, ascore+1 });
						memo[i][j] = ascore + 1;
					}
				}
				else{
					if (memo[i][j] < ascore){
						aStack.push({ i, j, ascore });
						memo[i][j] = ascore;
					}
				}
			}
		}
	}
	return 0;
}
int main(){
	e[0].resize(100);
	e[1].resize(100);
	int boardNums[2];
	rep(i, 2){
		int n, m;
		cin >> boardNums[i] >> m;
		rep(j, boardNums[i]){
			char c;
			cin >> c;
			int aalpha;
			if (find(AlphaCole.begin(), AlphaCole.end(), c) == AlphaCole.end()){
				AlphaCole.push_back(c);			
			}
			else{
			}
			aalpha = find(AlphaCole.begin(), AlphaCole.end(), c) - AlphaCole.begin();
			NodeColes[i].push_back({ j, aalpha });
			
			Tag[i][j] = aalpha;
		}
		rep(j, m){
			int a, b;
			cin >> a >> b;
			NodeColes[i][a - 1].ToCole.push_back(b - 1);
			e[i][a - 1].push_back(b - 1);
		}
	}
	vector<vector<int>>memo(boardNums[0], vector<int>(boardNums[1],0));
	
	rep(i, boardNums[0]){
		rep(j, boardNums[1]){
			if (Tag[0][i] == Tag[1][j]){
				memo[i][j] = 1;
				aStack.push({ i, j, 1, boardNums[0], (boardNums[1]) });
			}
			else
				aStack.push({ i, j, 0, boardNums[0], (boardNums[1]) });
		}
	}
	if (GetAns(memo, 0, 0) ){
		cout << -1 << endl;
		return 0;
	}
	vector<int>maxCol;
	rep(i, boardNums[0]){
		
		maxCol.push_back(*max_element(memo[i].begin(), memo[i].end()));
	}
	cout << *max_element(maxCol.begin(), maxCol.end())<<endl;
	return 0;
}
accuracy: 1
time_ms: 640
memory_kb: 8776
final_rank: 7

code_snippet: #include<iostream>
#include<vector>
#include<algorithm>
#include <iterator>
#include<queue>
#include <string>
#include <numeric>
#include<stack>
using namespace std;
#define rep(i,n) for(int i=0;i<(n);i++)
#define all(v) v.begin(),v.end()
vector< vector<int> > e[2];
struct Node{
	int id;
	int alpha;
	vector<int> ToCole;
};
struct To{
	int x1;
	int x2;
	int score;
	To(int a, int b, int c,int d ,int e){
		x1 = a; x2 = b;score = c;
	}
	To(int a, int b, int c){
		x1 = a; x2 = b; score = c;
	}
};
bool operator <(const To& T1, const To& T2){
	return T1.score < T2.score;
}
priority_queue < To > aStack;
vector<char> AlphaCole;
vector<vector<Node>> NodeColes(2);
vector<vector<int>> Tag(2,vector<int>(100));
int GetAns(vector<vector<int>>& memo, int p1, int p2){

	while (!aStack.empty()){
		p1 = aStack.top().x1;
		p2 = aStack.top().x2;
		int ascore = aStack.top().score;
		aStack.pop();
		if (ascore > 150){
			return -1;
		}
		if (Tag[0][p1] == Tag[1][p2]){
			for (auto i : e[0][p1]){
				for (auto j : e[1][p2]){
					if (Tag[0][i]==Tag[1][j]){
						if  (memo[i][j] <= ascore){
							aStack.push({ i, j, ascore+1 });
							memo[i][j] = ascore+1;
						}
					}
					else{
						if (memo[i][j] < ascore){
							aStack.push({ i, j, ascore });
							memo[i][j] = ascore;
						}
					}
				}
			}
		}
		else{
			for (auto i : e[0][p1]){
				for (auto j : e[1][p2]){
					if (Tag[0][i] == Tag[1][j]){
						if (memo[i][j] <= ascore){
							aStack.push({ i, j, ascore+1 });
							memo[i][j] = ascore + 1;
						}
					}
					else{
						if (memo[i][j] < ascore){
							aStack.push({ i, j, ascore });
							memo[i][j] = ascore;
						}
					}
				}
			}
			for (auto j : e[1][p2]){
				int i = p1;
				if (Tag[0][i] == Tag[1][j]){
					if (memo[i][j] <= ascore){
						aStack.push({ i, j, ascore+1 });
						memo[i][j] = ascore + 1;
					}
				}
				else{
					if (memo[i][j] < ascore){
						aStack.push({ i, j, ascore });
						memo[i][j] = ascore;
					}
				}
			}
			for (auto i : e[0][p1]){
				int j = p2;
				if (Tag[0][i] == Tag[1][j]){
					if (memo[i][j] <= ascore){
						aStack.push({ i, j, ascore+1 });
						memo[i][j] = ascore + 1;
					}
				}
				else{
					if (memo[i][j] < ascore){
						aStack.push({ i, j, ascore });
						memo[i][j] = ascore;
					}
				}
			}
		}
	}
	return 0;
}
int main(){
	e[0].resize(100);
	e[1].resize(100);
	int boardNums[2];
	rep(i, 2){
		int n, m;
		cin >> boardNums[i] >> m;
		rep(j, boardNums[i]){
			char c;
			cin >> c;
			int aalpha;
			if (find(AlphaCole.begin(), AlphaCole.end(), c) == AlphaCole.end()){
				AlphaCole.push_back(c);			
			}
			else{
			}
			aalpha = find(AlphaCole.begin(), AlphaCole.end(), c) - AlphaCole.begin();
			NodeColes[i].push_back({ j, aalpha });
			
			Tag[i][j] = aalpha;
		}
		rep(j, m){
			int a, b;
			cin >> a >> b;
			NodeColes[i][a - 1].ToCole.push_back(b - 1);
			e[i][a - 1].push_back(b - 1);
		}
	}
	vector<vector<int>>memo(boardNums[0], vector<int>(boardNums[1],0));
	
	rep(i, boardNums[0]){
		rep(j, boardNums[1]){
			if(Tag[0][i] == Tag[1][j])
				aStack.push({ i, j, 1, boardNums[0], (boardNums[1]) });
			else
				aStack.push({ i, j, 0, boardNums[0], (boardNums[1]) });
		}
	}
	if (GetAns(memo, 0, 0) ){
		cout << -1 << endl;
		return 0;
	}
	vector<int>maxCol;
	rep(i, boardNums[0]){
		
		maxCol.push_back(*max_element(memo[i].begin(), memo[i].end()));
	}
	cout << *max_element(maxCol.begin(), maxCol.end())<<endl;
	return 0;
}
accuracy: 0.25
time_ms: 10
memory_kb: 8884
final_rank: 16

code_snippet: #include<iostream>
#include<vector>
#include<algorithm>
#include <iterator>
#include<queue>
#include <string>
#include <numeric>
#include<stack>
using namespace std;
#define rep(i,n) for(int i=0;i<(n);i++)
#define all(v) v.begin(),v.end()
 
struct Node{
    int id;
    int alpha;
    vector<int> ToCole;
};
struct To{
    int x1;
    int x2;
    int score;
    vector<vector<int> > IsSelected;
    To(int a, int b, int c,int d ,int e):IsSelected(d,vector<int>(e)){
        x1 = a; x2 = b;score = c;
    }
    To(int a, int b, int c, vector<vector<int> > aIsSelected) :IsSelected(aIsSelected){
        x1 = a; x2 = b; score = c;
    }
};
stack < To > aStack;
vector<char> AlphaCole;
vector<vector<Node>> NodeColes(2);
int GetAns(vector<vector<int>>& memo, int p1, int p2){
     
    while (!aStack.empty()){
        p1 = aStack.top().x1;
        p2 = aStack.top().x2;
        int ascore = aStack.top().score;
        vector<vector<int> > aIsSelected(aStack.top().IsSelected);
        aStack.pop();
        Node Node1 = NodeColes[0][p1];
        Node Node2 = NodeColes[1][p2];
        if (aIsSelected[p1][p2]){
            if (ascore > memo[p1][p2] || (ascore == memo[p1][p2] && Node1.alpha == Node2.alpha)){
                return -1;
            }
            else{
                continue;
            }
        }
        if (ascore > 10000){
            return -1;
        }
 
         
        if (memo[p1][p2] <= ascore){
            int size1 = Node1.ToCole.size();
            int size2 = Node2.ToCole.size();
            if (Node1.alpha == Node2.alpha){
                ascore++;
                memo[p1][p2] = ascore;
                for (auto i : Node1.ToCole){
                    for (auto j : Node2.ToCole){
                        //if (aIsSelected[i][j])break;
                        aIsSelected[p1][p2] = 1;
                        aStack.push({ i, j, ascore, aIsSelected });
                        aIsSelected[p1][p2] =0;
                    }
                }
            }
            else{
                if (memo[p1][p2] == ascore)continue;
                memo[p1][p2] = ascore;
                for (auto i : Node1.ToCole){
                    for (auto j : Node2.ToCole){
                        //if (aIsSelected[i][j])break;
                        aIsSelected[p1][p2] = 1;
                        aStack.push({ i, j, ascore, aIsSelected });
                        aIsSelected[p1][p2] = 0;
                    }
                }
                for (auto j : Node2.ToCole){
                    //if (aIsSelected[p1][j])break;
                    aIsSelected[p1][p2] = 1;
                    aStack.push({ p1, j, ascore, aIsSelected });
                    aIsSelected[p1][p2] = 0;
                }
                for (auto i : Node1.ToCole){
                    //if (aIsSelected[i][p2])break;
                    aIsSelected[p1][p2] = 1;
                    aStack.push({ i, p2, ascore, aIsSelected });
                    aIsSelected[p1][p2] = 0;
                }
            }
        }
         
    }
    return 0;
}
int main(){
    int boardNums[2];
    rep(i, 2){
        int n, m;
        cin >> boardNums[i] >> m;
        rep(j, boardNums[i]){
            char c;
            cin >> c;
            int aalpha;
            if (find(AlphaCole.begin(), AlphaCole.end(), c) == AlphaCole.end()){
                AlphaCole.push_back(c);         
            }
            else{
            }
            aalpha = find(AlphaCole.begin(), AlphaCole.end(), c) - AlphaCole.begin();
            NodeColes[i].push_back({ j, aalpha });
        }
        rep(j, m){
            int a, b;
            cin >> a >> b;
            NodeColes[i][a - 1].ToCole.push_back(b - 1);
        }
    }
    vector<vector<int>>memo(boardNums[0], vector<int>(boardNums[1],0));
     
    rep(i, boardNums[0]){
        rep(j, boardNums[1]){
            aStack.push({ i, j, 0, boardNums[0], (boardNums[1]) });
            /*if (GetAns(memo, i, j, 0) == -1){
                cout << -1 << endl;
                return 0;*/
            //}
        }
    }
    if (GetAns(memo, 0, 0) ){
        cout << -1 << endl;
        return 0;
    }
    vector<int>maxCol;
    rep(i, boardNums[0]){
        maxCol.push_back(*max_element(memo[i].begin(), memo[i].end()));
    }
    cout << *max_element(maxCol.begin(), maxCol.end())<<endl;
    return 0;
}
accuracy: 0.17857142857142858
time_ms: 90
memory_kb: 98028
final_rank: 19

code_snippet: #include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <map>
#include <set>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <sstream>
#include <complex>
#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iterator>
#include <bitset>
#include <unordered_set>
#include <unordered_map>
#include <fstream>
#include <iomanip>
//#include <utility>
//#include <memory>
//#include <functional>
//#include <deque>
//#include <cctype>
//#include <ctime>
//#include <numeric>
//#include <list>
//#include <iomanip>

//#if __cplusplus >= 201103L
//#include <array>
//#include <tuple>
//#include <initializer_list>
//#include <forward_list>
//
//#define cauto const auto&
//#else

//#endif

using namespace std;


typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

typedef vector<int> vint;
typedef vector<vector<int> > vvint;
typedef vector<long long> vll, vLL;
typedef vector<vector<long long> > vvll, vvLL;

#define VV(T) vector<vector< T > >

template <class T>
void initvv(vector<vector<T> > &v, int a, int b, const T &t = T()){
    v.assign(a, vector<T>(b, t));
}

template <class F, class T>
void convert(const F &f, T &t){
    stringstream ss;
    ss << f;
    ss >> t;
}

#undef _P
#define _P(...) (void)printf(__VA_ARGS__)
#define reep(i,a,b) for(int i=(a);i<(b);++i)
#define rep(i,n) reep((i),0,(n))
#define ALL(v) (v).begin(),(v).end()
#define PB push_back
#define F first
#define S second
#define mkp make_pair
#define RALL(v) (v).rbegin(),(v).rend()
#define DEBUG
#ifdef DEBUG
#define dump(x)  cout << #x << " = " << (x) << endl;
#define debug(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << " " << __FILE__ << endl;
#else
#define dump(x) 
#define debug(x) 
#endif
#define LDcout(x,n) fixed<<setprecision(n)<<x

#define MOD 1000000007LL
#define EPS 1e-8
static const int INF=1<<24;
vector<char> v1,v2;
int dp[100][100];
int loop[100][100];
pii start;
bool ok=true;
int ans=0;
//?????£?????????????§£
class SCC{
	#define ZERO(a) memset(a,0,sizeof(a))
public:
	static const int MV = 5000;
	vector<vector<int> > SC; int NV,GR[MV];
public:
	vector<int> E[MV],RE[MV],NUM; int vis[MV];
public:
	void init(int NV) {this->NV=NV;for(int i=0;i<MV;i++){ E[i].clear();RE[i].clear();}}
	void add_edge(int x,int y){ E[x].push_back(y); RE[y].push_back(x); }
	void dfs(int cu) {vis[cu]=1;for(int i=0;i<E[cu].size();i++) if(!vis[E[cu][i]]) dfs(E[cu][i]); NUM.push_back(cu);}
	void revdfs(int cu,int ind){ vis[cu]=1;GR[cu]=ind; SC[ind].push_back(cu);
		for(int i=0;i<RE[cu].size();i++) if(!vis[RE[cu][i]]) revdfs(RE[cu][i],ind);}
	void scc(){
		int c=0; SC.clear(); SC.resize(MV); NUM.clear();
		ZERO(vis); for(int i=0;i<NV;i++) if(!vis[i]) dfs(i);
		ZERO(vis); for(int i=NUM.size()-1;i>=0;i--) if(!vis[NUM[i]]){
			SC[c].clear(); revdfs(NUM[i],c); sort(SC[c].begin(),SC[c].end()); c++;
		}
		SC.resize(c);
	}
};
int rec(int x,int y,vvint &cnt1,vvint &cnt2,vvint &b1,vvint &b2,vvint &a1,vvint &a2){
	if(dp[x][y]>=0) return dp[x][y];
	int ret=0;
	bool f=false;
	rep(i,256){
		if(cnt1[x][i]&&cnt2[y][i]){
			f=true;
		}
	}
	for(int i:b1[x]){
		for(int j:b2[y]){
			if(f&&i==x&&a1[x].size()==1) continue;
			if(f&&j==y&&a2[y].size()==1) continue;
			if(i==x&&j==y) continue;
			ret=max(ret,rec(i,j,cnt1,cnt2,b1,b2,a1,a2));
		}
	}
	ret+=f;
	return dp[x][y]=ret;
}
SCC vv1,vv2;
void mainmain(){
	rep(i,100) rep(j,100) dp[i][j]=-1;
	int n,m;
	cin>>n>>m;
	v1=vector<char>(n);
	vv1.init(n);
	rep(i,n) cin>>v1[i];
	rep(i,n){
		vv1.add_edge(i,i);
	}
	rep(i,m){
		int a,b;
		cin>>a>>b;
		vv1.add_edge(a-1,b-1);
	}
	cin>>n>>m;
	v2=vector<char>(n);
	vv2.init(n);
	rep(i,n) cin>>v2[i];
	rep(i,n){
		vv2.add_edge(i,i);
	}
	rep(i,m){
		int a,b;
		cin>>a>>b;
		vv2.add_edge(a-1,b-1);
	}
	vv1.scc();
	vv2.scc();
	vvint a1=vv1.SC;
	vvint a2=vv2.SC;
	vvint cnt1(a1.size(),vector<int>(256,0));
	vvint cnt2(a2.size(),vector<int>(256,0));
	rep(i,a1.size()) rep(j,a1[i].size()) cnt1[i][v1[a1[i][j]]]++;
	rep(i,a2.size()) rep(j,a2[i].size()) cnt2[i][v2[a2[i][j]]]++;
	bool ok=true;
	rep(i,a1.size()){
		rep(j,a2.size()){
			rep(k,256){
				if(a1[i].size()>=2&&a2[j].size()>=2&&cnt1[i][k]&&cnt2[j][k]) ok=false;
			}
		}
	}
	if(!ok){
		puts("-1");
		return;
	}
	vvint b1(a1.size()),b2(a2.size());
	vint num1(v1.size()),num2(v2.size());
	rep(i,a1.size()){
		rep(j,a1[i].size()){
			num1[a1[i][j]]=i;
		}
	}
	rep(i,a2.size()){
		rep(j,a2[i].size()){
			num2[a2[i][j]]=i;
		}
	}
	rep(i,a1.size()) b1[i].PB(i);
	rep(i,a2.size()) b2[i].PB(i);
	rep(i,5000){
		for(int j:vv1.E[i]){
			if(num1[i]==num1[j]) continue;
			b1[num1[i]].PB(num1[j]);
		}
	}
	rep(i,5000){
		for(int j:vv2.E[i]){
			if(num2[i]==num2[j]) continue;
			b2[num2[i]].PB(num2[j]);
		}
	}
	int ans=0;
	rep(i,a1.size()){
		rep(j,a2.size()){
			ans=max(ans,rec(i,j,cnt1,cnt2,b1,b2,a1,a2));
		}
	}
	cout<<ans<<endl;
}


signed main() {
	ios_base::sync_with_stdio(false);
  	cout<<fixed<<setprecision(0);
    mainmain();
}
accuracy: 1
time_ms: 150
memory_kb: 2444
final_rank: 2

code_snippet: #include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <map>
#include <set>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <sstream>
#include <complex>
#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iterator>
#include <bitset>
#include <unordered_set>
#include <unordered_map>
#include <fstream>
#include <iomanip>
//#include <utility>
//#include <memory>
//#include <functional>
//#include <deque>
//#include <cctype>
//#include <ctime>
//#include <numeric>
//#include <list>
//#include <iomanip>

//#if __cplusplus >= 201103L
//#include <array>
//#include <tuple>
//#include <initializer_list>
//#include <forward_list>
//
//#define cauto const auto&
//#else

//#endif

using namespace std;


typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

typedef vector<int> vint;
typedef vector<vector<int> > vvint;
typedef vector<long long> vll, vLL;
typedef vector<vector<long long> > vvll, vvLL;

#define VV(T) vector<vector< T > >

template <class T>
void initvv(vector<vector<T> > &v, int a, int b, const T &t = T()){
    v.assign(a, vector<T>(b, t));
}

template <class F, class T>
void convert(const F &f, T &t){
    stringstream ss;
    ss << f;
    ss >> t;
}

#undef _P
#define _P(...) (void)printf(__VA_ARGS__)
#define reep(i,a,b) for(int i=(a);i<(b);++i)
#define rep(i,n) reep((i),0,(n))
#define ALL(v) (v).begin(),(v).end()
#define PB push_back
#define F first
#define S second
#define mkp make_pair
#define RALL(v) (v).rbegin(),(v).rend()
#define DEBUG
#ifdef DEBUG
#define dump(x)  cout << #x << " = " << (x) << endl;
#define debug(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << " " << __FILE__ << endl;
#else
#define dump(x) 
#define debug(x) 
#endif
#define LDcout(x,n) fixed<<setprecision(n)<<x

#define MOD 1000000007LL
#define EPS 1e-8
static const int INF=1<<24;
vector<char> v1,v2;
int dp[100][100];
int loop[100][100];
pii start;
bool ok=true;
int ans=0;
//?????£?????????????§£
class SCC{
	#define ZERO(a) memset(a,0,sizeof(a))
public:
	static const int MV = 5000;
	vector<vector<int> > SC; int NV,GR[MV];
public:
	vector<int> E[MV],RE[MV],NUM; int vis[MV];
public:
	void init(int NV) {this->NV=NV;for(int i=0;i<MV;i++){ E[i].clear();RE[i].clear();}}
	void add_edge(int x,int y){ E[x].push_back(y); RE[y].push_back(x); }
	void dfs(int cu) {vis[cu]=1;for(int i=0;i<E[cu].size();i++) if(!vis[E[cu][i]]) dfs(E[cu][i]); NUM.push_back(cu);}
	void revdfs(int cu,int ind){ vis[cu]=1;GR[cu]=ind; SC[ind].push_back(cu);
		for(int i=0;i<RE[cu].size();i++) if(!vis[RE[cu][i]]) revdfs(RE[cu][i],ind);}
	void scc(){
		int c=0; SC.clear(); SC.resize(MV); NUM.clear();
		ZERO(vis); for(int i=0;i<NV;i++) if(!vis[i]) dfs(i);
		ZERO(vis); for(int i=NUM.size()-1;i>=0;i--) if(!vis[NUM[i]]){
			SC[c].clear(); revdfs(NUM[i],c); sort(SC[c].begin(),SC[c].end()); c++;
		}
		SC.resize(c);
	}
};
class UF {
	public:
	static const int ufmax=100052;
	int ufpar[ufmax],ufrank[ufmax],ufcnt[ufmax];
	UF() { init();}
	void init(){int i; rep(i,ufmax) { ufpar[i]=i; ufrank[i]=0; ufcnt[i]=1; } }
	int find(int x) {	return (ufpar[x]==x)?(x):(ufpar[x] = find(ufpar[x]));}
	int operator[](int x) {return find(x);}
	int count(int x) {return ufcnt[find(x)];}
	void unite(int x,int y) {
		x = find(x); y = find(y);
		if(x==y) return;
		if(ufrank[x]<ufrank[y]) ufpar[x]=y, ufcnt[y]+=ufcnt[x];
		else {ufpar[y]=x; ufcnt[x]+=ufcnt[y]; if(ufrank[x]==ufrank[y]) ufrank[x]++;}
	}
};
int rec(int x,int y,vvint &cnt1,vvint &cnt2,vvint &b1,vvint &b2,vvint &a1,vvint &a2){
	if(dp[x][y]>=0) return dp[x][y];
	// cout<<x<<" "<<y<<endl;
	int ret=0;
	bool f=false;
	rep(i,256){
		if(cnt1[x][i]&&cnt2[y][i]){
			f=true;
		}
	}
	for(int i:b1[x]){
		for(int j:b2[y]){
			if(f&&i==x&&a1[x].size()==1) continue;
			if(f&&j==y&&a2[y].size()==1) continue;
			if(i==x&&j==y) continue;
			ret=max(ret,rec(i,j,cnt1,cnt2,b1,b2,a1,a2));
		}
	}
	ret+=f;
	return dp[x][y]=ret;
}
SCC vv1,vv2;
void mainmain(){
	rep(i,100) rep(j,100) dp[i][j]=-1;
	int n,m;
	cin>>n>>m;
	v1=vector<char>(n);
	vv1.init(n);
	rep(i,n) cin>>v1[i];
	rep(i,n){
		vv1.add_edge(i,i);
	}
	rep(i,m){
		int a,b;
		cin>>a>>b;
		vv1.add_edge(a-1,b-1);
		// vv1[a-1].PB(b-1);
	}
	cin>>n>>m;
	// vv2=vvint(n);
	v2=vector<char>(n);
	vv2.init(n);
	rep(i,n) cin>>v2[i];
	rep(i,n){
		vv2.add_edge(i,i);
		// vv2[i].PB(i);
	}
	rep(i,m){
		int a,b;
		cin>>a>>b;
		vv2.add_edge(a-1,b-1);
		// vv2[a-1].PB(b-1);
	}
	// cout<<vv1.G.size()<<endl;
	vv1.scc();
	vv2.scc();
	vvint a1=vv1.SC;
	vvint a2=vv2.SC;
	vvint cnt1(a1.size(),vector<int>(256,0));
	vvint cnt2(a2.size(),vector<int>(256,0));
	// cout<<a1.size()<<endl;
	// rep(i,a1[0].size()) cout<<v1[a1[0][i]]<<endl;
	rep(i,a1.size()) rep(j,a1[i].size()) cnt1[i][v1[a1[i][j]]]++;
	rep(i,a2.size()) rep(j,a2[i].size()) cnt2[i][v2[a2[i][j]]]++;
	bool ok=true;
	rep(i,a1.size()){
		rep(j,a2.size()){
			rep(k,256){
				if(a1[i].size()>=2&&a2[j].size()>=2&&cnt1[i][k]&&cnt2[j][k]) ok=false;
			}
		}
	}
	if(!ok){
		puts("-1");
		return;
	}
	UF uf1,uf2;
	rep(i,a1.size()){
		rep(j,a1[i].size()){
			if(j+1==a1[i].size()) continue;
			uf1.unite(a1[i][j],a1[i][j+1]);
		}
	}
	rep(i,a2.size()){
		rep(j,a2[i].size()){
			if(j+1==a2[i].size()) continue;
			uf2.unite(a2[i][j],a2[i][j+1]);
		}
	}
	vvint b1(a1.size()),b2(a2.size());
	vint num1(v1.size()),num2(v2.size());
	rep(i,a1.size()){
		rep(j,a1[i].size()){
			num1[a1[i][j]]=i;
		}
	}
	rep(i,a2.size()){
		rep(j,a2[i].size()){
			num2[a2[i][j]]=i;
		}
	}
	rep(i,a1.size()) b1[i].PB(i);
	rep(i,a2.size()) b2[i].PB(i);
	rep(i,5000){
		for(int j:vv1.E[i]){
			if(num1[i]==num1[j]) continue;
			b1[num1[i]].PB(num1[j]);
		}
	}
	rep(i,5000){
		for(int j:vv2.E[i]){
			if(num2[i]==num2[j]) continue;
			b2[num2[i]].PB(num2[j]);
		}
	}
	int ans=0;
	// rep(i,b1.size())rep(j,b1[i].size()) cout<<i<<" "<<b1[i][j]<<endl;
	// cout<<endl;
	// rep(i,b2.size())rep(j,b2[i].size()) cout<<i<<" "<<b2[i][j]<<endl;
	rep(i,a1.size()){
		rep(j,a2.size()){
			// cout<<"s "<<i<<" "<<j<<endl;
			ans=max(ans,rec(i,j,cnt1,cnt2,b1,b2,a1,a2));
		}
	}
	cout<<ans<<endl;
}


signed main() {
	ios_base::sync_with_stdio(false);
  	cout<<fixed<<setprecision(0);
    mainmain();
}
accuracy: 1
time_ms: 150
memory_kb: 4796
final_rank: 3