Problem:
Palindrome Generator
A palidrome craftsperson starts to work in the early morning, 
with the clear air allowing him to polish up his palindromes.
On this morning, he is making his pieces to submit to the International Contest for Palindrome Craftspeople.
By the way, in order to make palindromes, he uses a special dictionary which contains a set of words and a set of ordered pairs of the words.
Any words and any ordered pairs of consecutive words in his palindromes must appear in the dictionary.
We already have his dictionary, so let's estimate how long a palindrome he can make.
Input
The first line in the data set consists of two integers
N
(
1 \leq N \leq 100
) and
M
(
0 \leq M \leq 1\,000
).
N
describes the number of words in the dictionary and
M
describes the number of ordered pairs of words.
The following
N
lines describe the words he can use.
The
i
-th line (
1
-based) contains the word
i
, which consists of only lower-case letters and whose length is between
1
and
10
, inclusive.
The following
M
lines describe the ordered pairs of consecutive words he can use.
The
j
-th line (
1
-based) contains two integers
X_j
and
Y_j
(
1 \leq X_j, Y_j \leq N
).
X_j
describes the (
1
-based) index of the former word in the palindrome and
Y_j
describes that of the latter word.
Output
Print the maximal length of the possible palindrome in a line.
If he can make no palidromes, print "0".
If he can make arbitrary long palindromes, print "-1".
Sample Input 1
2 2
ab
ba
1 1
1 2
Output for the Sample Input 1
4
Sample Input 2
2 2
ab
ba
1 1
2 2
Output for the Sample Input 2
0
Sample Input 3
2 2
ab
a
1 1
1 2
Output for the Sample Input 3
-1

Submitted solutions:

code_snippet: #include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <sstream>
#include <iomanip>
#include <queue>
#include <ctime>
using namespace std;
template <class T> void checkmin(T &t,T x){if (x < t) t = x;}
template <class T> void checkmax(T &t,T x){if (x > t) t = x;}
template <class T> void _checkmin(T &t, T x){if (t == -1) t = x; if (x < t) t = x;}
template <class T> void _checkmax(T &t, T x){if (t == -1) t = x; if (x > t) t = x;}
typedef pair <int,int> PII;
typedef pair <double,double> PDD;
typedef long long lld;
#define foreach(it,v) for (__typeof((v).begin()) it = (v).begin();it != (v).end();it++)
#define DEBUG(a) cout << #a" = " << (a) << endl;
#define DEBUGARR(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }
const int N = 105;
const int M = 300005;
int n, m, cnt;
string s[N], rs[N];
vector <int> next_arr[N];
vector <int> pre[N];
vector <PII> E[M];
int v[M];
int st[M];
int du[M];
int f[M];
int fa[M];
int wei[M];
int palind[N][11];
int rpalind[N][11];
int pos[N][N][11][2];
set <PII> Set;

int ans;

int abs(int x) {
	return x < 0 ? -x : x;
}

int find(int x, int y) {
	return Set.count(make_pair(x, y));
}

vector <PII> getEdge(int i, int j, int l, int d) {
	vector <PII> ret;
	if (d) {
		if (l > s[j].size()) return ret;
		if (find(i, j) && rpalind[j][s[j].size() - l]) {
			ret.push_back(make_pair(cnt, l));
		}
		foreach (it, next_arr[i]) {
			int len = min(l, (int)s[*it].size());
			if (s[*it].substr(0, len) == rs[j].substr(s[j].size() - l, len)) {
				ret.push_back(make_pair(pos[*it][j][abs(l - (int)s[*it].size())][l >= s[*it].size()], len * 2));
			}
		}
	} else if (l) {
		if (l > s[i].size()) return ret;
		if (find(i, j) && palind[i][s[i].size() - l]) {
			ret.push_back(make_pair(cnt, l));
		}
		foreach (it, pre[j]) {
			int len = min(l, (int)s[*it].size());
			if (rs[*it].substr(0, len) == s[i].substr(s[i].size() - l, len)) {
				ret.push_back(make_pair(pos[i][*it][abs(l - (int)s[*it].size())][s[*it].size() >= l], len * 2));
			}
		}
	}
	return ret;
}

void build_graph() {
	cnt = 0;
	for (int i = 0; i < n; i++) 
		for (int j = 0; j < n; j++)
			for (int k = 0; k < 11; k++)
				for (int l = 0; l < 2; l++)
					pos[i][j][k][l] = cnt++;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j <= s[i].size(); j++) {
			int k = s[i].size() - 1;
			string _s = s[i];
			int _i = j;
			int _j = k;
			int ok = 1;
			while (_i < _j) {
				if (_s[_i++] != _s[_j--]) {
					ok = 0;
					break;
				}
			}
			palind[i][j] = ok;
			_i = j;
			_j = k;
			_s = rs[i];
			ok = 1;
			while (_i < _j) {
				if (_s[_i++] != _s[_j--]) {
					ok = 0;
					break;
				}
			}
			rpalind[i][j] = ok;
		}
		if (palind[i][0]) {
			checkmax(ans, (int)s[i].size());
		}
	}
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			for (int k = 0; k < 11; k++)
				for (int l = 0; l < 2; l++) {
					E[pos[i][j][k][l]] = getEdge(i, j, k, l);
				}
}

void bfs() {
	queue <int> q;
	memset(v, 0, sizeof(v));
	memset(st, 0, sizeof(st));
	for (int i = 0; i <= cnt; i++) v[i] = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			int len = min(s[i].size(), s[j].size());
			if (s[i].substr(0, len) == rs[j].substr(0, len)) {
				int p = pos[i][j][abs((int)s[i].size() - (int)s[j].size())][s[j].size() >= s[i].size()];
				q.push(p);
				v[p] = 1;
				st[p] = 1;
				f[p] = min(s[i].size(), s[j].size()) * 2;
			}
		}
	}
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		foreach (it, E[x]) {
			if (!v[it->first]) {
				fa[it->first] = x;
				v[it->first] = 1;
				q.push(it->first);
			}
		}
	}
}

vector <int> q;

void topsort() {
	memset(du, 0, sizeof(du));
	q.clear();
	for (int i = 0; i <= cnt; i++) {
		if (v[i]) {
			foreach (it, E[i]) {
				if (v[it->first]) {
					du[it->first]++;
				}
			}
		}
	}
	int head = 0;
	for (int i = 0; i <= cnt; i++) {
		if (v[i] && !du[i]) {
			q.push_back(i);
		}
	}
	while (head != q.size()) {
		int x = q[head++];
		foreach (it, E[x]) {
			if (v[it->first]) {
				du[it->first]--;
				if (!du[it->first]) {
					q.push_back(it->first);
				}
			}
		}
	}
}

void dp() {
	if (!v[cnt] && ans == -1) {
		puts("0");
	} else if (du[cnt]) {
		puts("-1");
	} else {
		for (int i = 0; i < q.size(); i++) {
			int x = q[i];
			if (f[x] != -1) {
				foreach (it, E[x]) {
					checkmax(f[it->first], f[x] + it->second);
				}
			}
		}
		cout << max(f[cnt], ans) << endl;
	}
}

int main(){
#ifdef cwj
	freopen("in", "r", stdin);
#endif
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		cin >> s[i];
		rs[i] = s[i];
		reverse(rs[i].begin(), rs[i].end());
	}
	Set.clear();
	memset(f, 0xFF, sizeof(f));
	for (int i = 0; i < m; i++) {
		int x, y;
		cin >> x >> y;
		x--; y--;
		Set.insert(make_pair(x, y));
		next_arr[x].push_back(y);
		pre[y].push_back(x);
	}
	ans = -1;
	build_graph();
	bfs();
	topsort();
	dp();
}
accuracy: 1
time_ms: 30
memory_kb: 21912
final_rank: 3

code_snippet: // time complexity: O(nml) where l = (maximum length of a word)

#include <array>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

struct edge_base {
	int s, t, w;
};

struct edge {
	int t, w;
};

bool check(string s, int pos) {
	int n = s.size();
	bool res = true;
	if (pos % 2 == 0) {
		for (int i = 0; i < min(pos / 2, n - pos / 2); i++) {
			res = res && (s[pos / 2 - i - 1] == s[pos / 2 + i]);
		}
	} else {
		for (int i = 1; i <= min(pos / 2, n - pos / 2 - 1); i++) {
			res = res && (s[pos / 2 - i] == s[pos / 2 + i]);
		}
	}
	return res;
}

vector<bool> reachable(int N, vector<vector<edge> > G, vector<int> start) {
	vector<bool> ans(N, false);
	vector<int> st;
	for (int i = 0; i < N; i++) {
		if (start[i] != -1) {
			ans[i] = true;
			st.push_back(i);
		}
	}
	while (!st.empty()) {
		int u = st.back();
		st.pop_back();
		for (edge e : G[u]) {
			if (!ans[e.t]) {
				ans[e.t] = true;
				st.push_back(e.t);
			}
		}
	}
	return ans;
}

int longest_path(int N, vector<edge_base> es, vector<int> start, vector<int> goal) {
	// step #1. construct graph
	vector<vector<edge> > G1(N), G2(N);
	for (edge_base e : es) {
		G1[e.s].push_back(edge{e.t, e.w});
		G2[e.t].push_back(edge{e.s, e.w});
	}

	// step #2. reduce graph
	vector<bool> reach1 = reachable(N, G1, start);
	vector<bool> reach2 = reachable(N, G2, goal);
	vector<bool> reach(N);
	for (int i = 0; i < N; i++) {
		reach[i] = reach1[i] && reach2[i];
	}
	vector<vector<edge> > G3(N);
	vector<int> deg(N);
	for (edge_base e : es) {
		if (reach[e.s] && reach[e.t]) {
			G3[e.s].push_back(edge{e.t, e.w});
			deg[e.t]++;
		}
	}

	// step #3. compute in topological order
	vector<int> dp(N);
	vector<int> st;
	for (int i = 0; i < N; i++) {
		if (start[i] != -1 && deg[i] == 0) {
			dp[i] = start[i];
			st.push_back(i);
		}
	}
	int ans = 0;
	while (!st.empty()) {
		int u = st.back();
		st.pop_back();
		if (goal[u] != -1) {
			ans = max(ans, dp[u] + goal[u]);
		}
		for (edge e : G3[u]) {
			deg[e.t]--;
			dp[e.t] = max(dp[e.t], dp[u] + e.w);
			if (deg[e.t] == 0) {
				st.push_back(e.t);
			}
		}
	}
	if (deg != vector<int>(N, 0)) {
		return -1;
	}

	return ans;
}

int solve(int N, int M, vector<string> S, vector<edge_base> es) {
	// step #1. preparation
	vector<int> L(N);
	for (int i = 0; i < N; i++) {
		L[i] = S[i].size();
	}
	vector<vector<int> > G1(N), G2(N);
	for (edge_base e : es) {
		G1[e.s].push_back(e.t);
		G2[e.t].push_back(e.s);
	}
	vector<array<int, 3> > states;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			for (int k = -L[i]; k <= +L[j]; k++) {
				states.push_back({i, j, k});
			}
		}
	}
	int Z = states.size();
	auto search_state = [&](array<int, 3> v) {
		int pos = lower_bound(states.begin(), states.end(), v) - states.begin();
		if (pos == Z || states[pos] != v) {
			return -1;
		}
		return pos;
	};

	// step #2. compute all "start/goal states"
	vector<int> start(Z, -1), goal(Z, -1);
	for (int i = 0; i < Z; i++) {
		if (states[i][0] == states[i][1] && check(S[states[i][0]], -states[i][2] + L[states[i][0]])) {
			start[i] = L[states[i][0]];
		}
		if (states[i][2] == 0) {
			goal[i] = 0;
		}
	}

	// step #3. construct graph
	vector<edge_base> eg;
	for (int i = 0; i < Z; i++) {
		array<int, 3> s = states[i];
		// add to left
		if (s[2] >= 0) {
			for (int j : G2[s[0]]) {
				bool ok = true;
				for (int k = 0; k < s[2] && k < L[j]; k++) {
					ok = ok && (S[s[1]][L[s[1]] - s[2] + k] == S[j][L[j] - k - 1]);
				}
				if (ok) {
					int z = search_state({j, s[1], s[2] - L[j]});
					eg.push_back(edge_base{i, z, L[j]});
				}
			}
		}
		// add to right
		if (s[2] <= 0) {
			for (int j : G1[s[1]]) {
				bool ok = true;
				for (int k = 0; k < -s[2] && k < L[j]; k++) {
					ok = ok && (S[s[0]][-s[2] - k - 1] == S[j][k]);
				}
				if (ok) {
					int z = search_state({s[0], j, s[2] + L[j]});
					eg.push_back(edge_base{i, z, L[j]});
				}
			}
		}
	}

	// step #4. compute answer
	int ans = longest_path(Z, eg, start, goal);

	return ans;
}

int main() {
	int N, M;
	cin >> N >> M;
	vector<string> S(N);
	for (int i = 0; i < N; i++) {
		cin >> S[i];
	}
	vector<edge_base> es(M);
	for (int i = 0; i < M; i++) {
		cin >> es[i].s >> es[i].t;
		es[i].s--; es[i].t--;
	}
	int ans = solve(N, M, S, es);
	cout << ans << endl;
	return 0;
}
accuracy: 1
time_ms: 80
memory_kb: 40224
final_rank: 7

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

#define rep(i, n) for (int i = 0; i < (n); i++)
#define per(i, n) for (int i = (n)-1; i >= 0; i--)
#define rep2(i, l, r) for (int i = (l); i < (r); i++)
#define per2(i, l, r) for (int i = (r)-1; i >= (l); i--)
#define each(e, x) for (auto &e : x)
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)
#define sz(x) (int)x.size()
#define eb emplace_back
#define MM << ' ' <<
#define TT template <typename T>
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;

TT bool chmin(T &x, T y) { return x > y ? (x = y, true) : false; }
TT bool chmax(T &x, T y) { return x < y ? (x = y, true) : false; }

TT using minheap = priority_queue<T, vector<T>, greater<T>>;
TT using maxheap = priority_queue<T>;

TT void print(vector<T> v, T x = 0) {
    int n = sz(v);
    rep(i, n) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
    if (n == 0) cout << '\n';
}

TT void printn(vector<T> v, T x = 0) {
    int n = sz(v);
    rep(i, n) cout << v[i] + x << '\n';
}

TT void rearrange(vector<T> &v) {
    sort(all(v));
    v.erase(unique(all(v)), end(v));
}

TT int lb(vector<T> &v, T x) { return lower_bound(all(v), x) - begin(v); }
TT int ub(vector<T> &v, T x) { return upper_bound(all(v), x) - begin(v); }

const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

vector<int> scc(vector<vector<int>> es) {
    int n = sz(es);
    vector<vector<int>> rs(n);
    rep(i, n) each(e, es[i]) rs[e].eb(i);

    vector<int> comp(n, -1);
    vector<int> vs;

    auto dfs = [&](auto &&dfs, int now) -> void {
        if (comp[now] != -1) return;
        comp[now] = 1;
        each(e, es[now]) dfs(dfs, e);
        vs.eb(now);
    };

    auto rdfs = [&](auto &&rdfs, int now, int col) -> void {
        if (comp[now] != -1) return;
        comp[now] = col;
        each(e, rs[now]) rdfs(rdfs, e, col);
    };

    rep(i, n) dfs(dfs, i);
    fill(all(comp), -1);
    reverse(all(vs));
    int cnt = 0;
    each(e, vs) {
        if (comp[e] == -1) rdfs(rdfs, e, cnt++);
    }
    return comp;
}

int main() {
    int N, M;
    cin >> N >> M;

    vector<string> S(N + 1);
    rep(i, N) cin >> S[i];

    vector<vector<int>> adj(N + 1, vector<int>(N + 1, 0));
    rep(i, M) {
        int u, v;
        cin >> u >> v;
        u--, v--;
        adj[u][v] = 1;
    }

    rep(i, N + 1) adj[i][N] = adj[N][i] = 1;

    int L = 11;

    int V = 2 * (N + 1) * (N + 1) * L;

    auto tupleToInt = [&](int i, int j, int f, int l) {
        int ret = 0;
        ret *= N + 1, ret += i;
        ret *= N + 1, ret += j;
        ret *= 2, ret += f;
        ret *= L, ret += l;
        return ret;
    };

    vector<vector<int>> es(V + 1);
    vector<vector<ll>> weight(V + 1);

    rep(i, N + 1) rep(j, N + 1) rep(f, 2) rep(l, L) {
        int u = tupleToInt(i, j, f, l);
        if (f == 0) {
            if (l > sz(S[i])) continue;
            rep(k, N) {
                if (!adj[k][j]) continue;
                int m = sz(S[k]);
                if (m <= l) {
                    string X = S[i];
                    reverse(all(X));
                    if (X.substr(l - m, m) != S[k]) continue;
                    int v = tupleToInt(i, k, 0, l - m);
                    es[u].eb(v);
                    weight[u].eb(m);
                } else {
                    string X = S[i];
                    reverse(all(X));
                    if (X.substr(0, l) != S[k].substr(m - l, l)) continue;
                    int v = tupleToInt(i, k, 1, m - l);
                    es[u].eb(v);
                    weight[u].eb(l);
                }
            }
        } else {
            if (l > sz(S[j])) continue;
            rep(k, N) {
                if (!adj[i][k]) continue;
                int m = sz(S[k]);
                if (m < l) {
                    string X = S[k];
                    reverse(all(X));
                    if (S[j].substr(l - m, m) != X) continue;
                    int v = tupleToInt(k, j, 1, l - m);
                    es[u].eb(v);
                    weight[u].eb(m);
                } else {
                    string X = S[k];
                    reverse(all(X));
                    if (S[j].substr(0, l) != X.substr(m - l, l)) continue;
                    int v = tupleToInt(k, j, 0, m - l);
                    es[u].eb(v);
                    weight[u].eb(l);
                }
            }
        }
    }

    auto comp = scc(es);
    int K = sz(comp);

    vector<vector<pll>> dagEs(K);
    vector<int> deg(K, 0);
    rep(i, V + 1) {
        int T = sz(es[i]);
        assert(sz(weight[i]) == T);
        rep(j, T) {
            int u = comp[i], v = comp[es[i][j]];
            if (u == v) continue;
            ll w = weight[i][j];
            dagEs[u].eb(v, w);
            deg[v]++;
        }
    }

    vector<int> cnt(K, 0);
    rep(i, V + 1) cnt[comp[i]]++;

    vector<ll> d(K, -INF);
    queue<int> que;
    d[comp[tupleToInt(N, N, 0, 0)]] = 0;

    rep(i, K) {
        if (deg[i] == 0) que.emplace(i);
    }

    while (!empty(que)) {
        int i = que.front();
        if (cnt[i] >= 2 && d[i] != -INF) d[i] = INF;
        que.pop();
        for (auto [j, w] : dagEs[i]) {
            if (d[i] == INF) {
                chmax(d[j], INF);
            } else if (d[i] != -INF) {
                chmax(d[j], d[i] + w);
            }
            if (--deg[j] == 0) que.emplace(j);
        }
    }

    ll ans = -INF;

    auto isPalindrome = [](string S) {
        auto T = S;
        reverse(all(T));
        return S == T;
    };

    rep(i, N + 1) rep(j, N + 1) rep(f, 2) rep(l, L) {
        int v = comp[tupleToInt(i, j, f, l)];
        // if (d[v] == 4) cout << i MM j MM f MM l << endl;
        if (!adj[i][j]) continue;

        if (f == 0) {
            if (l > sz(S[i])) continue;
            if (isPalindrome(S[i].substr(sz(S[i]) - l, l))) {
                chmax(ans, d[v] * 2 + l); //
            }
        } else {
            if (l > sz(S[j])) continue;
            if (isPalindrome(S[j].substr(0, l))) {
                chmax(ans, d[v] * 2 + l); //
            }
        }
    }

    cout << (ans >= INF ? -1 : ans) << '\n';
}
accuracy: 1
time_ms: 110
memory_kb: 44788
final_rank: 9

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

#define rep(i, n) for (int i = 0; i < (n); i++)
#define per(i, n) for (int i = (n)-1; i >= 0; i--)
#define rep2(i, l, r) for (int i = (l); i < (r); i++)
#define per2(i, l, r) for (int i = (r)-1; i >= (l); i--)
#define each(e, x) for (auto &e : x)
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)
#define sz(x) (int)x.size()
#define eb emplace_back
#define MM << ' ' <<
#define TT template <typename T>
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;

TT bool chmin(T &x, T y) { return x > y ? (x = y, true) : false; }
TT bool chmax(T &x, T y) { return x < y ? (x = y, true) : false; }

TT using minheap = priority_queue<T, vector<T>, greater<T>>;
TT using maxheap = priority_queue<T>;

TT void print(vector<T> v, T x = 0) {
    int n = sz(v);
    rep(i, n) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
    if (n == 0) cout << '\n';
}

TT void printn(vector<T> v, T x = 0) {
    int n = sz(v);
    rep(i, n) cout << v[i] + x << '\n';
}

TT void rearrange(vector<T> &v) {
    sort(all(v));
    v.erase(unique(all(v)), end(v));
}

TT int lb(vector<T> &v, T x) { return lower_bound(all(v), x) - begin(v); }
TT int ub(vector<T> &v, T x) { return upper_bound(all(v), x) - begin(v); }

const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

vector<int> scc(vector<vector<int>> es) {
    int n = sz(es);
    vector<vector<int>> rs(n);
    rep(i, n) each(e, es[i]) rs[e].eb(i);

    vector<int> comp(n, -1);
    vector<int> vs;

    auto dfs = [&](auto &&dfs, int now) -> void {
        if (comp[now] != -1) return;
        comp[now] = 1;
        each(e, es[now]) dfs(dfs, e);
        vs.eb(now);
    };

    auto rdfs = [&](auto &&rdfs, int now, int col) -> void {
        if (comp[now] != -1) return;
        comp[now] = col;
        each(e, rs[now]) rdfs(rdfs, e, col);
    };

    rep(i, n) dfs(dfs, i);
    fill(all(comp), -1);
    reverse(all(vs));
    int cnt = 0;
    each(e, vs) {
        if (comp[e] == -1) rdfs(rdfs, e, cnt++);
    }
    return comp;
}

int main() {
    int N, M;
    cin >> N >> M;

    vector<string> S(N + 1);
    rep(i, N) cin >> S[i];

    vector<vector<int>> adj(N + 1, vector<int>(N + 1, 0));
    rep(i, M) {
        int u, v;
        cin >> u >> v;
        u--, v--;
        adj[u][v] = 1;
    }

    rep(i, N + 1) adj[i][N] = adj[N][i] = 1;

    int L = 10;

    int V = 2 * (N + 1) * (N + 1) * L;

    auto tupleToInt = [&](int i, int j, int f, int l) {
        int ret = 0;
        ret *= N + 1, ret += i;
        ret *= N + 1, ret += j;
        ret *= 2, ret += f;
        ret *= L, ret += l;
        return ret;
    };

    vector<vector<int>> es(V + 1);
    vector<vector<ll>> weight(V + 1);

    rep(i, N + 1) rep(j, N + 1) rep(f, 2) rep(l, L) {
        int u = tupleToInt(i, j, f, l);
        if (f == 0) {
            if (l > sz(S[i])) continue;
            rep(k, N) {
                if (!adj[k][j]) continue;
                int m = sz(S[k]);
                if (m <= l) {
                    string X = S[i];
                    reverse(all(X));
                    if (X.substr(l - m, m) != S[k]) continue;
                    int v = tupleToInt(i, k, 0, l - m);
                    es[u].eb(v);
                    weight[u].eb(m);
                } else {
                    string X = S[i];
                    reverse(all(X));
                    if (X.substr(0, l) != S[k].substr(m - l, l)) continue;
                    int v = tupleToInt(i, k, 1, m - l);
                    es[u].eb(v);
                    weight[u].eb(l);
                }
            }
        } else {
            if (l > sz(S[j])) continue;
            rep(k, N) {
                if (!adj[i][k]) continue;
                int m = sz(S[k]);
                if (m < l) {
                    string X = S[k];
                    reverse(all(X));
                    if (S[j].substr(l - m, m) != X) continue;
                    int v = tupleToInt(k, j, 1, l - m);
                    es[u].eb(v);
                    weight[u].eb(m);
                } else {
                    string X = S[k];
                    reverse(all(X));
                    if (S[j].substr(0, l) != X.substr(m - l, l)) continue;
                    int v = tupleToInt(k, j, 0, m - l);
                    es[u].eb(v);
                    weight[u].eb(l);
                }
            }
        }
    }

    auto comp = scc(es);
    int K = sz(comp);

    vector<vector<pll>> dagEs(K);
    vector<int> deg(K, 0);
    rep(i, V + 1) {
        int T = sz(es[i]);
        assert(sz(weight[i]) == T);
        rep(j, T) {
            int u = comp[i], v = comp[es[i][j]];
            if (u == v) continue;
            ll w = weight[i][j];
            dagEs[u].eb(v, w);
            deg[v]++;
        }
    }

    vector<int> cnt(K, 0);
    rep(i, V + 1) cnt[comp[i]]++;

    vector<ll> d(K, -INF);
    queue<int> que;
    d[comp[tupleToInt(N, N, 0, 0)]] = 0;

    rep(i, K) {
        if (deg[i] == 0) que.emplace(i);
    }

    while (!empty(que)) {
        int i = que.front();
        if (cnt[i] >= 2 && d[i] != -INF) d[i] = INF;
        que.pop();
        for (auto [j, w] : dagEs[i]) {
            if (d[i] == INF) {
                chmax(d[j], INF);
            } else if (d[i] != -INF) {
                chmax(d[j], d[i] + w);
            }
            if (--deg[j] == 0) que.emplace(j);
        }
    }

    ll ans = -INF;

    auto isPalindrome = [](string S) {
        auto T = S;
        reverse(all(T));
        return S == T;
    };

    rep(i, N + 1) rep(j, N + 1) rep(f, 2) rep(l, L) {
        int v = comp[tupleToInt(i, j, f, l)];
        // if (d[v] == 4) cout << i MM j MM f MM l << endl;
        if (!adj[i][j]) continue;

        if (f == 0) {
            if (l > sz(S[i])) continue;
            if (isPalindrome(S[i].substr(sz(S[i]) - l, l))) {
                chmax(ans, d[v] * 2 + l); //
            }
        } else {
            if (l > sz(S[j])) continue;
            if (isPalindrome(S[j].substr(0, l))) {
                chmax(ans, d[v] * 2 + l); //
            }
        }
    }

    cout << (ans >= INF ? -1 : ans) << '\n';
}
accuracy: 0.15306122448979592
time_ms: 90
memory_kb: 31768
final_rank: 20

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

// Normal Vaeiables
int N; string S[1009];
int M, X[1009], Y[1009];
bool Valid[109][109];

// Dynamic Programming
int Time = -1;
int Last = -1;
int dp[109][109][2][19];
tuple<int, int, int, int> NexL[109][109][19];
tuple<int, int, int, int> NexR[109][109][19];
priority_queue<tuple<int, int, int, int, int>, vector<tuple<int, int, int, int, int>>, greater<tuple<int, int, int, int, int>>> Q;

void Get_NexL(int new_, int old_, int nag) {
    string str1 = S[new_]; reverse(str1.begin(), str1.end());
    string str2 = S[old_].substr((int)S[old_].size() - nag, nag);
    if (str1.size() < str2.size()) str2 = str2.substr(0, str1.size());
    if (str1.size() > str2.size()) str1 = str1.substr(0, str2.size());

    // Invalid Case
    if (str1 != str2) {
        NexL[new_][old_][nag] = make_tuple(-1, -1, -1, -1);
        return;
    }

    // Valid Case
    int Len1 = S[new_].size();
    int Len2 = nag;
    if (Len1 >= Len2) NexL[new_][old_][nag] = make_tuple(new_, old_, 0, Len1 - Len2);
    if (Len1 <  Len2) NexL[new_][old_][nag] = make_tuple(new_, old_, 1, Len2 - Len1);
}

void Get_NexR(int old_, int new_, int nag) {
    string str2 = S[old_].substr(0, nag); reverse(str2.begin(), str2.end());
    string str1 = S[new_];
    if (str1.size() < str2.size()) str2 = str2.substr(0, str1.size());
    if (str1.size() > str2.size()) str1 = str1.substr(0, str2.size());

    // Invalid Case
    if (str1 != str2) {
        NexR[old_][new_][nag] = make_tuple(-1, -1, -1, -1);
        return;
    }

    // Valid Case
    int Len1 = nag;
    int Len2 = S[new_].size();
    if (Len1 >= Len2) NexR[old_][new_][nag] = make_tuple(old_, new_, 0, Len1 - Len2);
    if (Len1 <  Len2) NexR[old_][new_][nag] = make_tuple(old_, new_, 1, Len2 - Len1);
}

void Update(int pos1, int pos2, int typ, int nag) {
    // Left is Longer
    if (typ == 0) {
        for (int i = 1; i <= N; i++) {
            if (Valid[pos2][i] == false) continue;
            if (NexR[pos1][i][nag] == make_tuple(-1, -1, -1, -1)) continue;
            int nex1 = get<0>(NexR[pos1][i][nag]);
            int nex2 = get<1>(NexR[pos1][i][nag]);
            int nexT = get<2>(NexR[pos1][i][nag]);
            int nexN = get<3>(NexR[pos1][i][nag]);
            if (dp[nex1][nex2][nexT][nexN] < dp[pos1][pos2][typ][nag] + (int)S[i].size()) {
                dp[nex1][nex2][nexT][nexN] = dp[pos1][pos2][typ][nag] + (int)S[i].size();
                Q.push(make_tuple(dp[nex1][nex2][nexT][nexN], nex1, nex2, nexT, nexN));
                if (nexN == 0) Last = clock() - Time;
            }
        }
    }

    // Right is Longer
    if (typ == 1) {
        for (int i = 1; i <= N; i++) {
            if (Valid[i][pos1] == false) continue;
            if (NexL[i][pos2][nag] == make_tuple(-1, -1, -1, -1)) continue;
            int nex1 = get<0>(NexL[i][pos2][nag]);
            int nex2 = get<1>(NexL[i][pos2][nag]);
            int nexT = get<2>(NexL[i][pos2][nag]);
            int nexN = get<3>(NexL[i][pos2][nag]);
            if (dp[nex1][nex2][nexT][nexN] < dp[pos1][pos2][typ][nag] + (int)S[i].size()) {
                dp[nex1][nex2][nexT][nexN] = dp[pos1][pos2][typ][nag] + (int)S[i].size();
                Q.push(make_tuple(dp[nex1][nex2][nexT][nexN], nex1, nex2, nexT, nexN));
                if (nexN == 0) Last = clock() - Time;
            }
        }
    }
}

int main() {
    // Step 1. Input
    cin >> N >> M;
    for (int i = 1; i <= N; i++) cin >> S[i];
    for (int i = 1; i <= M; i++) cin >> X[i] >> Y[i];
    for (int i = 1; i <= M; i++) Valid[X[i]][Y[i]] = true;

    // Step 2. DP Init 1
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) {
            for (int k = 0; k < 2; k++) {
                for (int l = 0; l <= 10; l++) dp[i][j][k][l] = -(1 << 30);
            }
        }
    }

    // Step 3. DP Init 2
    for (int i = 1; i <= N; i++) {
        for (int j = 0; j <= (int)S[i].size(); j++) {
            string str1 = S[i].substr(0, j); reverse(str1.begin(), str1.end());
            string str2 = S[i].substr(j, S[i].size() - j);
            if (str1.size() < str2.size()) str2 = str2.substr(0, str1.size());
            if (str1.size() > str2.size()) str1 = str1.substr(0, str2.size());
            if (str1 != str2) continue;
            int Len1 = j;
            int Len2 = S[i].size() - j;
            int Type = (Len1 >= Len2 ? 0 : 1);
            int Naga = abs(Len1 - Len2);
            dp[i][i][Type][Naga] = S[i].size();
            Q.push(make_tuple((int)S[i].size(), i, i, Type, Naga));
        }
    }

    // Step 3. DP Init 3
    for (int i = 1; i <= N; i++) {
        for (int j = 0; j <= (int)S[i].size() - 1; j++) {
            string str1 = S[i].substr(0, j); reverse(str1.begin(), str1.end());
            string str2 = S[i].substr(j + 1, S[i].size() - (j + 1));
            if (str1.size() < str2.size()) str2 = str2.substr(0, str1.size());
            if (str1.size() > str2.size()) str1 = str1.substr(0, str2.size());
            if (str1 != str2) continue;
            int Len1 = j;
            int Len2 = S[i].size() - (j + 1);
            int Type = (Len1 >= Len2 ? 0 : 1);
            int Naga = abs(Len1 - Len2);
            dp[i][i][Type][Naga] = S[i].size();
            Q.push(make_tuple((int)S[i].size(), i, i, Type, Naga));
        }
    }


    // Step 4. DP Init 3
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) {
            for (int k = 0; k <= (int)S[j].size(); k++) Get_NexL(i, j, k);
            for (int k = 0; k <= (int)S[i].size(); k++) Get_NexR(i, j, k);
        }
    }

    // Step 5. DP Start
    Time = clock();
    while (!Q.empty()) {
        int curr = get<0>(Q.top());
        int pos1 = get<1>(Q.top());
        int pos2 = get<2>(Q.top());
        int typ  = get<3>(Q.top());
        int nag  = get<4>(Q.top()); Q.pop();
        if (curr != dp[pos1][pos2][typ][nag]) continue;

        // Update
        Update(pos1, pos2, typ, nag);
        if (clock() - Time >= 45 * CLOCKS_PER_SEC / 10) break;
    }

    // Step 6. Output
    if (Last >= 30 * CLOCKS_PER_SEC / 10) cout << "-1" << endl;
    else {
        int Answer = 0;
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= N; j++) {
                Answer = max(Answer, dp[i][j][0][0]);
                Answer = max(Answer, dp[i][j][1][0]);
            }
        }
        cout << Answer << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 4500
memory_kb: 12304
final_rank: 12

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return true; } return false; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return true; } return false; }
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define mp make_pair
#define si(x) int(x.size())
const int mod=998244353,MAX=210005,INF=1<<28;

int V,cmp[MAX];
vector<int> G[MAX],rG[MAX],vs;//vsがトポソの逆順になってる
bool used[MAX];

int get_id(int i,bool f){
    return 2*i+f;
}

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

void either(int i,bool f,int j,bool g){
    add_edge(get_id(i,!f),get_id(j,g));
    add_edge(get_id(j,!g),get_id(i,f));
    //add_edge(2*i+(!f),2*j+g);
    //add_edge(2*j+(!g),2*i+f);
}

void imply(int i,bool f,int j,bool g){
    either(i,!f,j,g);
}
//iがfならばjがg

void must(int i,bool f){
    either(i,f,i,f);
}

void DFS(int v){
    used[v]=1;
    for(int i=0;i<G[v].size();i++){
        if(used[G[v][i]]==0) DFS(G[v][i]);
    }
    vs.push_back(v);
}

void rDFS(int v,int k){
    used[v]=1;
    cmp[v]=k;
    for(int i=0;i<rG[v].size();i++){
        if(used[rG[v][i]]==0) rDFS(rG[v][i],k);
    }
}

int scc(){
    memset(used,0,sizeof(used));
    vs.clear();
    for(int v=0;v<V;v++){
        if(used[v]==0) DFS(v);
    }
    
    memset(used,0,sizeof(used));
    int k=0;
    for(int i=vs.size()-1;i>=0;i--){
        if(used[vs[i]]==0) rDFS(vs[i],k++);
    }
    return k;
}

void init(int sz){
    V=sz;
    for(int i=0;i<V;i++){
        cmp[i]=0;
        G[i].clear();
        rG[i].clear();
        used[i]=false;
    }
    vs.clear();
}

bool E[105][105];

vector<pair<int,int>> sv[MAX],T[MAX];

bool cycle[MAX];

int dp[MAX];

int main(){
    
    std::ifstream in("text.txt");
    std::cin.rdbuf(in.rdbuf());
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    int N,M;cin>>N>>M;
    auto idx=[&](int l,int r,bool left,int len){
        int res=0;
        res+=l*N*21;
        res+=r*21;
        if(len==0){
            
        }else if(left){
            res+=len;
        }else{
            res+=10+len;
        }
        return res;
    };
    
    vector<string> S(N);
    for(int i=0;i<N;i++) cin>>S[i];
    
    for(int i=0;i<M;i++){
        int a,b;cin>>a>>b;a--;b--;
        E[a][b]=true;
    }
    
    for(int l=0;l<N;l++){
        for(int r=0;r<N;r++){
            for(int lef=0;lef<2;lef++){
                for(int len=0;len<=10;len++){
                    if(lef==0&&len==0) continue;
                    if(lef==1){
                        if(len>si(S[l])) continue;
                    }else{
                        if(len>si(S[r])) continue;
                    }
                    
                    int a=idx(l,r,lef,len);
                    
                    if(len==0){
                        for(int x=0;x<N;x++){
                            if(E[x][l]){
                                int b=idx(x,r,1,si(S[x]));
                                add_edge(a,b);
                                sv[a].push_back(mp(b,si(S[x])));
                            }
                        }
                    }else if(lef){
                        for(int x=0;x<N;x++){
                            if(E[r][x]){
                                bool ok=true;
                                for(int i=0;i<min(len,si(S[x]));i++){
                                    ok&=(S[l][len-1-i]==S[x][i]);
                                }
                                if(ok){
                                    if(len>=si(S[x])){
                                        int b=idx(l,x,1,len-si(S[x]));
                                        add_edge(a,b);
                                        sv[a].push_back(mp(b,si(S[x])));
                                    }else{
                                        int b=idx(l,x,0,si(S[x])-len);
                                        add_edge(a,b);
                                        sv[a].push_back(mp(b,si(S[x])));
                                    }
                                }
                            }
                        }
                    }else{
                        for(int x=0;x<N;x++){
                            if(E[x][l]){
                                bool ok=true;
                                for(int i=0;i<min(len,si(S[x]));i++){
                                    ok&=(S[x][si(S[x])-1-i]==S[r][si(S[r])-len+i]);
                                }
                                if(ok){
                                    if(len>si(S[x])){
                                        int b=idx(x,r,0,len-si(S[x]));
                                        add_edge(a,b);
                                        sv[a].push_back(mp(b,si(S[x])));
                                    }else{
                                        int b=idx(x,r,1,si(S[x])-len);
                                        add_edge(a,b);
                                        sv[a].push_back(mp(b,si(S[x])));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    int st=N*N*21;
    for(int x=0;x<N;x++){
        for(int k=0;k<=si(S[x]);k++){
            bool ok=true;
            for(int i=0;i<min(k,si(S[x])-k);i++){
                ok&=(S[x][k-1-i]==S[x][k+i]);
            }
            if(ok){
                int a=st;
                if(k<=si(S[x])-k){
                    int b=idx(x,x,0,si(S[x])-k-k);
                    add_edge(a,b);
                    sv[a].push_back(mp(b,si(S[x])));
                }else{
                    int b=idx(x,x,1,k-(si(S[x])-k));
                    add_edge(a,b);
                    sv[a].push_back(mp(b,si(S[x])));
                }
            }
        }
        for(int k=0;k<si(S[x]);k++){
            bool ok=true;
            for(int i=0;i<min(k,si(S[x])-1-k);i++){
                ok&=(S[x][k-1-i]==S[x][k+1+i]);
            }
            if(ok){
                int a=st;
                if(k<=si(S[x])-1-k){
                    int b=idx(x,x,0,si(S[x])-1-k-k);
                    add_edge(a,b);
                    sv[a].push_back(mp(b,si(S[x])));
                }else{
                    int b=idx(x,x,1,k-(si(S[x])-1-k));
                    add_edge(a,b);
                    sv[a].push_back(mp(b,si(S[x])));
                }
            }
        }
    }
    V=st+1;
    int K=scc();
    vector<int> sz(K);
    for(int i=0;i<=st;i++){
        sz[cmp[i]]++;
    }
    
    for(int i=0;i<K;i++){
        if(sz[i]>=2) cycle[i]=true;
    }
    
    for(int i=0;i<V;i++){
        for(auto [j,len]:sv[i]){
            if(cmp[i]!=cmp[j]){
                assert(cmp[i]<cmp[j]);
                T[cmp[i]].push_back(mp(cmp[j],len));
            }
        }
    }
    
    for(int i=0;i<K;i++) dp[i]=-INF;
    dp[cmp[st]]=0;
    
    for(int i=0;i<K;i++){
        if(dp[i]>=0){
            if(cycle[i]) dp[i]=INF;
            
            for(auto [j,len]:T[i]){
                chmax(dp[j],dp[i]+len);
            }
        }
    }
    
    int ans=-INF;
    
    for(int l=0;l<N;l++){
        for(int r=0;r<N;r++){
            int a=idx(l,r,0,0);
            chmax(ans,dp[cmp[a]]);
        }
    }
    
    if(ans<0) cout<<0<<"\n";
    else if(ans>=INF) cout<<-1<<"\n";
    else cout<<ans<<"\n";
}
accuracy: 1
time_ms: 60
memory_kb: 40996
final_rank: 8

code_snippet: //#pragma GCC optimize("O3")
//#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
const ll mod = 998244353;
const ll INF = mod * mod;
typedef pair<int, int>P;

#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;
typedef long double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-8;
const ld pi = acosl(-1.0);

ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	ll n;
	modint() :n(0) { ; }
	modint(ll m) :n(m) {
		if (n >= mod)n %= mod;
		else if (n < 0)n = (n % mod + mod) % mod;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 2;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}


vector<string> s;
vector<vector<int>> G, rG;
vector<P> vp;
vector<int> ad;
bool e[100][100];


struct graph {
private:
	int n;
	vector<bool> used;
	vector<int> vs;

	int mk;
	vector<vector<int>> fG;
	vector<vector<int>> ori;
	vector<int> trans;
public:
	graph(int sz) {
		n = sz;
		used.resize(n);

		fG.resize(n);
		trans.resize(n, -1);
		ori.resize(n);
	}
	void dfs(int v) {
		used[v] = true;
		int i = v / vp.size();
		int j = v % vp.size();
		int id1 = vp[i].first;
		int loc1 = vp[i].second;
		int id2 = vp[j].first;
		int loc2 = vp[j].second;

		if (loc1 > 0 && loc2 + 1 < s[id2].size()) {
			if (s[id1][loc1 - 1] == s[id2][loc2 + 1]) {
				int to = (i - 1) * vp.size() + j + 1;
				if (!used[to])dfs(to);
			}
		}
		else if (loc1 > 0) {
			for (int idto : G[id2]) {
				if (s[id1][loc1 - 1] == s[idto][0]) {
					int to = (ad[id1] + loc1 - 1) * vp.size() + ad[idto] + 0;

					if (!used[to])dfs(to);
				}
			}
		}
		else if (loc2 + 1 < s[id2].size()) {
			for (int idto : rG[id1]) {
				if (s[idto].back() == s[id2][loc2 + 1]) {
					int to = (ad[idto] + s[idto].size() - 1) * vp.size() + ad[id2] + loc2 + 1;

					if (!used[to])dfs(to);
				}
			}
		}
		else {
			for (int idto1 : rG[id1]) {
				for (int idto2 : G[id2]) {
					if (s[idto1].back() == s[idto2][0]) {
						int to = (ad[idto1] + s[idto1].size() - 1) * vp.size() + ad[idto2] + 0;

						if (!used[to])dfs(to);
					}
				}
			}
		}
		vs.push_back(v);
	}
	void rdfs(int v, int k) {
		used[v] = true;
		queue<int> q; q.push(v);
		vector<int> c;
		while (!q.empty()) {
			int id = q.front(); q.pop();
			ori[k].push_back(id);
			int i = id % vp.size();
			int j = id / vp.size();
			int id1 = vp[i].first;
			int loc1 = vp[i].second;
			int id2 = vp[j].first;
			int loc2 = vp[j].second;
			if (s[id1][loc1] == s[id2][loc2]) {
				if (loc1 > 0 && loc2 + 1 < s[id2].size()) {
					//if (s[id1][loc1 - 1] == s[id2][loc2 + 1]) {
					int to = (i - 1) + (j + 1) * vp.size();
					if (used[to]) {
						if (trans[to] >= 0)c.push_back(trans[to]);
					}
					else {
						used[to] = true;
						q.push(to);
					}
					//}
				}
				else if (loc1 > 0) {
					for (int idto : G[id2]) {
						//if (s[id1][loc1 - 1] == s[idto][0]) {
						int to = (ad[id1] + loc1 - 1) + (ad[idto] + 0) * vp.size();
						if (used[to]) {
							if (trans[to] >= 0)c.push_back(trans[to]);
						}
						else {
							used[to] = true;
							q.push(to);
						}
						//}
					}
				}
				else if (loc2 + 1 < s[id2].size()) {
					for (int idto : rG[id1]) {
						//if (s[idto].back() == s[id2][loc2 + 1]) {
						int to = (ad[idto] + s[idto].size() - 1) + (ad[id2] + loc2 + 1) * vp.size();
						if (used[to]) {
							if (trans[to] >= 0)c.push_back(trans[to]);
						}
						else {
							used[to] = true;
							q.push(to);
						}
						//}
					}
				}
				else {
					for (int idto1 : rG[id1]) {
						for (int idto2 : G[id2]) {
							//if (s[idto1].back() == s[idto2][0]) {
							int to = (ad[idto1] + s[idto1].size() - 1) + (ad[idto2] + 0) * vp.size();
							if (used[to]) {
								if (trans[to] >= 0)c.push_back(trans[to]);
							}
							else {
								used[to] = true;
								q.push(to);
							}
							//}
						}
					}
				}
			}
		}
		sort(c.begin(), c.end());
		int len = unique(c.begin(), c.end()) - c.begin();
		rep(i, len) {
			fG[c[i]].push_back(k);
		}
		rep(i, ori[k].size()) {
			trans[ori[k][i]] = k;
		}
	}
	void scc() {
		fill(used.begin(), used.end(), false);
		rep(i, n) {
			if (!used[i])dfs(i);
		}
		fill(used.begin(), used.end(), false);
		int k = 0;
		per(i, (int)vs.size()) {
			if (!used[vs[i]]) {
				rdfs(vs[i], k); k++;
			}
		}
		mk = k;
	}
	int query(vector<P> ps) {
		vector<int> lens(s.size());
		rep(i, s.size())lens[i] = s[i].size();
		int m = lens.size();
		vector<int> ad(m + 1);
		rep(i, m)ad[i + 1] = ad[i] + lens[i];
		int sum = ad[m];
		vector<bool> canfin(n);
		rep(i, m)rep(j, m) {
			int id = ad[i] * sum + ad[j] + lens[j] - 1;
			canfin[id] = true;
		}
		vector<P> ans(mk);
		per(i, mk) {
			bool exifin = false;
			for (int id : ori[i]) {
				if (canfin[id])exifin = true;
			}
			for (int to : fG[i]) {
				if (ans[to].second)exifin = true;
			}
			if (!exifin) {
				ans[i] = { 0,0 };
				continue;
			}
			ans[i].second = 1;
			int ma = 0;
			for (int to : fG[i]) {
				if (ans[to].second) {
					ma = max(ma, ans[to].first + 2);
				}
			}
			ans[i].first = ma;
			bool exiinf = false;
			if (ori[i].size() > 1)exiinf = true;
			else {
				int id = ori[i][0];
				int id1 = id / vp.size();
				int id2 = id % vp.size();
				id1 = vp[id1].first;
				id2 = vp[id2].first;
				if (s[id1].size() == 1 && s[id2].size() == 1 && s[id1] == s[id2]&&e[id1][id1]&&e[id2][id2])exiinf = true;
			}
			if (exiinf)ans[i].first = mod;
		}
		int res = 0;
		//odd
		rep(i, m)rep(j, lens[i]) {
			int cur = (ad[i] + j) * sum + ad[i] + j;
			cur = trans[cur];
			if (ans[cur].second) {
				res = max(res, ans[cur].first + 1);
			}
		}
		//even
		rep(i, m)rep(j, lens[i] - 1) {
			if (s[i][j] == s[i][j + 1]) {
				int cur = (ad[i] + j) * sum + ad[i] + j + 1;
				cur = trans[cur];
				if (ans[cur].second) {
					res = max(res, ans[cur].first + 2);
				}
			}
		}
		for(P p:ps){
			int i = p.first, j = p.second;
			if (s[i].back() == s[j][0]) {
				int cur = (ad[i] + lens[i] - 1) * sum + ad[j];
				cur = trans[cur];
				if (ans[cur].second) {
					//cout << "? "<<ori[cur].size()<<" "<<ori[cur][0] << " " << ans[cur].first << "\n";
					res = max(res, ans[cur].first + 2);
				}
			}
		}
		if (res >= mod)res = -1;
		return res;
	}
};

int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };

ll gcd(ll x, ll y) {
	x = abs(x), y = abs(y);
	if (x < y)swap(x, y);
	while (y) {
		ll r = x % y; x = y; y = r;
	}
	return x;
}

void solve() {
	int n, m; cin >> n >> m;
	s.resize(n);
	rep(i, n)cin >> s[i];
	ad.resize(n);
	rep(i, n) {
		ad[i] = vp.size();
		rep(j, s[i].size())vp.push_back({ i,j });
	}
	G.resize(n);
	rG.resize(n);
	vector<P> ps;
	rep(i, m) {
		int a, b; cin >> a >> b; a--; b--;
		G[a].push_back(b);
		rG[b].push_back(a);
		e[a][b] = true;
		ps.push_back({ a,b });
	}
	graph g(vp.size()*vp.size());
	g.scc();
	int ans = g.query(ps);
	cout << ans << "\n";
}



signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(10);
	//init_f();
	//init();
	//int t; cin >> t; rep(i, t)
	//while (cin >> n>>s,n)
		solve();
	return 0;
}
accuracy: 1
time_ms: 120
memory_kb: 100468
final_rank: 11

code_snippet: //#pragma GCC optimize("O3")
//#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
const ll mod = 998244353;
const ll INF = mod * mod;
typedef pair<int, int>P;

#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;
typedef long double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-8;
const ld pi = acosl(-1.0);

ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	ll n;
	modint() :n(0) { ; }
	modint(ll m) :n(m) {
		if (n >= mod)n %= mod;
		else if (n < 0)n = (n % mod + mod) % mod;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 2;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}


vector<string> s;
vector<vector<int>> G, rG;
vector<P> vp;
vector<int> ad;
bool e[100][100];


struct graph {
private:
	int n;
	vector<bool> used;
	vector<int> vs;

	int mk;
	vector<vector<int>> fG;
	vector<vector<int>> ori;
	vector<int> trans;
public:
	graph(int sz) {
		n = sz;
		used.resize(n);

		fG.resize(n);
		trans.resize(n, -1);
		ori.resize(n);
	}
	void dfs(int v) {
		used[v] = true;
		int i = v / vp.size();
		int j = v % vp.size();
		int id1 = vp[i].first;
		int loc1 = vp[i].second;
		int id2 = vp[j].first;
		int loc2 = vp[j].second;

		int cur = i * vp.size() + j;
		if (loc1 > 0 && loc2 + 1 < s[id2].size()) {
			if (s[id1][loc1 - 1] == s[id2][loc2 + 1]) {
				int to = (i - 1) * vp.size() + j + 1;
				if (!used[to])dfs(to);
			}
		}
		else if (loc1 > 0) {
			for (int idto : G[id2]) {
				if (s[id1][loc1 - 1] == s[idto][0]) {
					int to = (ad[id1] + loc1 - 1) * vp.size() + ad[idto] + 0;

					if (!used[to])dfs(to);
				}
			}
		}
		else if (loc2 + 1 < s[id2].size()) {
			for (int idto : rG[id1]) {
				if (s[idto].back() == s[id2][loc2 + 1]) {
					int to = (ad[idto] + s[idto].size() - 1) * vp.size() + ad[id2] + loc2 + 1;

					if (!used[to])dfs(to);
				}
			}
		}
		else {
			for (int idto1 : rG[id1]) {
				for (int idto2 : G[id2]) {
					if (s[idto1].back() == s[idto2][0]) {
						int to = (ad[idto1] + s[idto1].size() - 1) * vp.size() + ad[idto2] + 0;

						if (!used[to])dfs(to);
					}
				}
			}
		}
		vs.push_back(v);
	}
	void rdfs(int v, int k) {
		used[v] = true;
		queue<int> q; q.push(v);
		vector<int> c;
		while (!q.empty()) {
			int id = q.front(); q.pop();
			ori[k].push_back(id);
			int i = id % vp.size();
			int j = id / vp.size();
			int id1 = vp[i].first;
			int loc1 = vp[i].second;
			int id2 = vp[j].first;
			int loc2 = vp[j].second;

			int cur = i * vp.size() + j;
			if (loc1 > 0 && loc2 + 1 < s[id2].size()) {
				if (s[id1][loc1 - 1] == s[id2][loc2 + 1]) {
					int to = (i - 1)  + (j + 1) * vp.size();
					if (used[to]) {
						if (trans[to] >= 0)c.push_back(trans[to]);
					}
					else {
						used[to] = true;
						q.push(to);
					}
				}
			}
			else if (loc1 > 0) {
				for (int idto : G[id2]) {
					if (s[id1][loc1 - 1] == s[idto][0]) {
						int to = (ad[id1] + loc1 - 1) + (ad[idto] + 0) * vp.size();
						if (used[to]) {
							if (trans[to] >= 0)c.push_back(trans[to]);
						}
						else {
							used[to] = true;
							q.push(to);
						}
					}
				}
			}
			else if (loc2 + 1 < s[id2].size()) {
				for (int idto : rG[id1]) {
					if (s[idto].back() == s[id2][loc2 + 1]) {
						int to = (ad[idto] + s[idto].size() - 1) + (ad[id2] + loc2 + 1)*vp.size();
						if (used[to]) {
							if (trans[to] >= 0)c.push_back(trans[to]);
						}
						else {
							used[to] = true;
							q.push(to);
						}
					}
				}
			}
			else {
				for (int idto1 : rG[id1]) {
					for (int idto2 : G[id2]) {
						if (s[idto1].back() == s[idto2][0]) {
							int to = (ad[idto1] + s[idto1].size() - 1) + (ad[idto2] + 0)*vp.size();
							if (used[to]) {
								if (trans[to] >= 0)c.push_back(trans[to]);
							}
							else {
								used[to] = true;
								q.push(to);
							}
						}
					}
				}
			}
		}
		sort(c.begin(), c.end());
		int len = unique(c.begin(), c.end()) - c.begin();
		rep(i, len) {
			fG[c[i]].push_back(k);
		}
		rep(i, ori[k].size()) {
			trans[ori[k][i]] = k;
		}
	}
	void scc() {
		fill(used.begin(), used.end(), false);
		rep(i, n) {
			if (!used[i])dfs(i);
		}
		fill(used.begin(), used.end(), false);
		int k = 0;
		per(i, (int)vs.size()) {
			if (!used[vs[i]]) {
				rdfs(vs[i], k); k++;
			}
		}
		mk = k;
	}
	int query(vector<P> ps) {
		vector<int> lens(s.size());
		rep(i, s.size())lens[i] = s[i].size();
		int m = lens.size();
		vector<int> ad(m + 1);
		rep(i, m)ad[i + 1] = ad[i] + lens[i];
		int sum = ad[m];
		vector<bool> canfin(n);
		rep(i, m)rep(j, m) {
			int id = ad[i] * sum + ad[j] + lens[j] - 1;
			canfin[id] = true;
		}
		vector<P> ans(mk);
		per(i, mk) {
			bool exifin = false;
			for (int id : ori[i]) {
				if (canfin[id])exifin = true;
			}
			for (int to : fG[i]) {
				if (ans[to].second)exifin = true;
			}
			if (!exifin) {
				ans[i] = { 0,0 };
				continue;
			}
			ans[i].second = 1;
			int ma = 0;
			for (int to : fG[i]) {
				if (ans[to].second) {
					ma = max(ma, ans[to].first + 2);
				}
			}
			ans[i].first = ma;
			bool exiinf = false;
			if (ori[i].size() > 1)exiinf = true;
			else {
				int id = ori[i][0];
				int id1 = id / vp.size();
				int id2 = id % vp.size();
				id1 = vp[id1].first;
				id2 = vp[id2].first;
				if (s[id1].size() == 1 && s[id2].size() == 1 && s[id1] == s[id2]&&e[id1][id1]&&e[id2][id2])exiinf = true;
			}
			if (exiinf)ans[i].first = mod;
		}
		int res = 0;
		//odd
		rep(i, m)rep(j, lens[i]) {
			int cur = (ad[i] + j) * sum + ad[i] + j;
			cur = trans[cur];
			if (ans[cur].second) {
				res = max(res, ans[cur].first + 1);
			}
		}
		//even
		rep(i, m)rep(j, lens[i] - 1) {
			if (s[i][j] == s[i][j + 1]) {
				int cur = (ad[i] + j) * sum + ad[i] + j + 1;
				cur = trans[cur];
				if (ans[cur].second) {
					res = max(res, ans[cur].first + 2);
				}
			}
		}
		for(P p:ps){
			int i = p.first, j = p.second;
			if (s[i].back() == s[j][0]) {
				int cur = (ad[i] + lens[i] - 1) * sum + ad[j];
				cur = trans[cur];
				if (ans[cur].second) {
					//cout << "? "<<ori[cur].size()<<" "<<ori[cur][0] << " " << ans[cur].first << "\n";
					res = max(res, ans[cur].first + 2);
				}
			}
		}
		if (res >= mod)res = -1;
		return res;
	}
};

int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };

ll gcd(ll x, ll y) {
	x = abs(x), y = abs(y);
	if (x < y)swap(x, y);
	while (y) {
		ll r = x % y; x = y; y = r;
	}
	return x;
}

void solve() {
	int n, m; cin >> n >> m;
	s.resize(n);
	rep(i, n)cin >> s[i];
	ad.resize(n);
	rep(i, n) {
		ad[i] = vp.size();
		rep(j, s[i].size())vp.push_back({ i,j });
	}
	G.resize(n);
	rG.resize(n);
	vector<P> ps;
	rep(i, m) {
		int a, b; cin >> a >> b; a--; b--;
		G[a].push_back(b);
		rG[b].push_back(a);
		e[a][b] = true;
		ps.push_back({ a,b });
	}
	graph g(vp.size()*vp.size());
	g.scc();
	int ans = g.query(ps);
	cout << ans << "\n";
}



signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(10);
	//init_f();
	//init();
	//int t; cin >> t; rep(i, t)
	//while (cin >> n>>s,n)
		solve();
	return 0;
}
accuracy: 0.2653061224489796
time_ms: 120
memory_kb: 98532
final_rank: 19

code_snippet: #line 2 "/Users/nok0/Documents/Programming/nok0/graph/graph.hpp"
#include <algorithm>
#include <cassert>
#include <deque>
#include <iostream>
#include <queue>
#include <tuple>
#include <utility>
#include <vector>

struct Edge {
	int to;
	long long cost;
	Edge() = default;
	Edge(int to_, long long cost_) : to(to_), cost(cost_) {}
	bool operator<(const Edge &a) const { return cost < a.cost; }
	bool operator>(const Edge &a) const { return cost > a.cost; }
	friend std::ostream &operator<<(std::ostream &s, Edge &a) {
		s << "to: " << a.to << ", cost: " << a.cost;
		return s;
	}
};

class Graph {
	std::vector<std::vector<Edge>> edges;

	template <class F>
	struct rec_lambda {
		F f;
		rec_lambda(F &&f_) : f(std::forward<F>(f_)) {}
		template <class... Args>
		auto operator()(Args &&... args) const {
			return f(*this, std::forward<Args>(args)...);
		}
	};

public:
	inline const std::vector<Edge> &operator[](int k) const { return edges[k]; }
	inline std::vector<Edge> &operator[](int k) { return edges[k]; }

	int size() const { return edges.size(); }
	void resize(const int n) { edges.resize(n); }

	Graph() = default;
	Graph(int n) : edges(n) {}
	Graph(int n, int e, bool weight = 0, bool directed = 0, int idx = 1) : edges(n) { input(e, weight, directed, idx); }
	const long long INF = 3e18;

	void input(int e = -1, bool weight = 0, bool directed = false, int idx = 1) {
		if(e == -1) e = size() - 1;
		while(e--) {
			int u, v;
			long long cost = 1;
			std::cin >> u >> v;
			if(weight) std::cin >> cost;
			u -= idx, v -= idx;
			edges[u].emplace_back(v, cost);
			if(!directed) edges[v].emplace_back(u, cost);
		}
	}

	void add_edge(int u, int v, long long cost = 1, bool directed = false, int idx = 0) {
		u -= idx, v -= idx;
		edges[u].emplace_back(v, cost);
		if(!directed) edges[v].emplace_back(u, cost);
	}

	// Ο(V+E)
	std::vector<long long> bfs(int s) {
		std::vector<long long> dist(size(), INF);
		std::queue<int> que;
		dist[s] = 0;
		que.push(s);
		while(!que.empty()) {
			int v = que.front();
			que.pop();
			for(auto &e : edges[v]) {
				if(dist[e.to] != INF) continue;
				dist[e.to] = dist[v] + e.cost;
				que.push(e.to);
			}
		}
		return dist;
	}

	// Ο(V+E)
	// constraint: cost of each edge is zero or one
	std::vector<long long> zero_one_bfs(int s) {
		std::vector<long long> dist(size(), INF);
		std::deque<int> deq;
		dist[s] = 0;
		deq.push_back(s);
		while(!deq.empty()) {
			int v = deq.front();
			deq.pop_front();
			for(auto &e : edges[v]) {
				assert(0LL <= e.cost and e.cost < 2LL);
				if(e.cost and dist[e.to] > dist[v] + 1) {
					dist[e.to] = dist[v] + 1;
					deq.push_back(e.to);
				} else if(!e.cost and dist[e.to] > dist[v]) {
					dist[e.to] = dist[v];
					deq.push_front(e.to);
				}
			}
		}
		return dist;
	}

	// Ο((E+V)logV)
	// cannot reach: INF
	std::vector<long long> dijkstra(int s) {  // verified
		std::vector<long long> dist(size(), INF);
		const auto compare = [](const std::pair<long long, int> &a, const std::pair<long long, int> &b) { return a.first > b.first; };
		std::priority_queue<std::pair<long long, int>, std::vector<std::pair<long long, int>>, decltype(compare)> que{compare};
		dist[s] = 0;
		que.emplace(0, s);
		while(!que.empty()) {
			std::pair<long long, int> p = que.top();
			que.pop();
			int v = p.second;
			if(dist[v] < p.first) continue;
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					que.emplace(dist[e.to], e.to);
				}
			}
		}
		return dist;
	}

	// Ο(VE)
	// cannot reach: INF
	// negative cycle: -INF
	std::vector<long long> bellman_ford(int s) {  // verified
		int n = size();
		std::vector<long long> res(n, INF);
		res[s] = 0;
		for(int loop = 0; loop < n - 1; loop++) {
			for(int v = 0; v < n; v++) {
				if(res[v] == INF) continue;
				for(auto &e : edges[v]) {
					res[e.to] = std::min(res[e.to], res[v] + e.cost);
				}
			}
		}
		std::queue<int> que;
		std::vector<int> chk(n);
		for(int v = 0; v < n; v++) {
			if(res[v] == INF) continue;
			for(auto &e : edges[v]) {
				if(res[e.to] > res[v] + e.cost and !chk[e.to]) {
					que.push(e.to);
					chk[e.to] = 1;
				}
			}
		}
		while(!que.empty()) {
			int now = que.front();
			que.pop();
			for(auto &e : edges[now]) {
				if(!chk[e.to]) {
					chk[e.to] = 1;
					que.push(e.to);
				}
			}
		}
		for(int i = 0; i < n; i++)
			if(chk[i]) res[i] = -INF;
		return res;
	}

	// Ο(V^3)
	std::vector<std::vector<long long>> warshall_floyd() {  // verified
		int n = size();
		std::vector<std::vector<long long>> dist(n, std::vector<long long>(n, INF));
		for(int i = 0; i < n; i++) dist[i][i] = 0;
		for(int i = 0; i < n; i++)
			for(auto &e : edges[i]) dist[i][e.to] = std::min(dist[i][e.to], e.cost);
		for(int k = 0; k < n; k++)
			for(int i = 0; i < n; i++) {
				if(dist[i][k] == INF) continue;
				for(int j = 0; j < n; j++) {
					if(dist[k][j] == INF) continue;
					dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
				}
			}
		return dist;
	}

	// Ο(V) (using DFS)
	// if a directed cycle exists, return {}
	std::vector<int> topological_sort() {  // verified
		std::vector<int> res;
		int n = size();
		std::vector<int> used(n, 0);
		bool not_DAG = false;
		for(int i = 0; i < n; i++) {
			rec_lambda([&](auto &&dfs, int k) -> void {
				if(not_DAG) return;
				if(used[k]) {
					if(used[k] == 1) not_DAG = true;
					return;
				}
				used[k] = 1;
				for(auto &e : edges[k]) dfs(e.to);
				used[k] = 2;
				res.push_back(k);
			})(i);
		}
		if(not_DAG) return std::vector<int>{};
		std::reverse(res.begin(), res.end());
		return res;
	}

	bool is_DAG() { return !topological_sort().empty(); }  // verified

	// Ο(V)
	// array of the distance from each vertex to the most distant vertex
	std::vector<long long> height() {  // verified
		auto vec1 = bfs(0);
		int v1 = -1, v2 = -1;
		long long dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v1 = i;
		vec1 = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v2 = i;
		auto vec2 = bfs(v2);
		for(int i = 0; i < int(size()); i++)
			if(vec1[i] < vec2[i]) vec1[i] = vec2[i];
		return vec1;
	}

	// O(V+E)
	// vector<(int)(0 or 1)>
	// if it is not bipartite, return {}
	std::vector<int> bipartite_grouping() {
		std::vector<int> colors(size(), -1);
		auto dfs = [&](auto self, int now, int col) -> bool {
			colors[now] = col;
			for(auto &e : edges[now]) {
				if(col == colors[e.to]) return false;
				if(colors[e.to] == -1 and !self(self, e.to, !col)) return false;
			}
			return true;
		};
		for(int i = 0; i < int(size()); i++)
			if(colors[i] == -1 and !dfs(dfs, i, 0)) return std::vector<int>{};
		return colors;
	}

	bool is_bipartite() { return !bipartite_grouping().empty(); }

	// Ο(V+E)
	// ((v1, v2), diameter)
	std::pair<std::pair<int, int>, long long> diameter() {  // verified
		auto vec = bfs(0);
		int v1 = -1, v2 = -1;
		long long dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec[i]) dia = vec[i], v1 = i;
		vec = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec[i]) dia = vec[i], v2 = i;
		std::pair<std::pair<int, int>, long long> res = {{v1, v2}, dia};
		return res;
	}

	// Ο(V+E)
	// return {s, v1, v2, ... t}
	std::vector<int> diameter_path() {
		auto vec = bfs(0);
		int v1 = -1, v2 = -1;
		long long dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec[i]) dia = vec[i], v1 = i;
		auto vec2 = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec2[i]) dia = vec2[i], v2 = i;
		vec = bfs(v2);
		std::vector<int> ret;
		auto dfs = [&](auto self, int now, int p) -> void {
			ret.emplace_back(now);
			if(now == v2) return;
			for(auto &[to, cost] : (*this)[now]) {
				if(vec[to] + vec2[to] == dia and to != p)
					self(self, to, now);
			}
		};
		dfs(dfs, v1, -1);
		return ret;
	}

	// Ο(V+E)
	// return subtree_size, root = root
	std::vector<int> subtree_size(const int root) {
		int n = size();
		std::vector<int> ret(n, 1);
		rec_lambda([&](auto &&dfs, int now, int p) -> void {
			for(auto &[to, cost] : (*this)[now]) {
				if(to == p) continue;
				dfs(to, now);
				ret[now] += ret[to];
			}
		})(root, -1);
		return ret;
	}

	// Ο(ElogV)
	long long prim() {  // verified
		long long res = 0;
		std::priority_queue<Edge, std::vector<Edge>, std::greater<Edge>> que;
		for(auto &e : edges[0]) que.push(e);
		std::vector<int> chk(size());
		chk[0] = 1;
		int cnt = 1;
		while(cnt < size()) {
			auto e = que.top();
			que.pop();
			if(chk[e.to]) continue;
			cnt++;
			res += e.cost;
			chk[e.to] = 1;
			for(auto &e2 : edges[e.to]) que.push(e2);
		}
		return res;
	}

	// Ο(ElogE)
	long long kruskal() {  // verified
		std::vector<std::tuple<int, int, long long>> Edges;
		for(int i = 0; i < int(size()); i++)
			for(auto &e : edges[i]) Edges.emplace_back(i, e.to, e.cost);
		std::sort(Edges.begin(), Edges.end(), [](const std::tuple<int, int, long long> &a, const std::tuple<int, int, long long> &b) {
			return std::get<2>(a) < std::get<2>(b);
		});
		std::vector<int> uf_data(size(), -1);
		auto root = [&uf_data](auto self, int x) -> int {
			if(uf_data[x] < 0) return x;
			return uf_data[x] = self(self, uf_data[x]);
		};
		auto unite = [&uf_data, &root](int u, int v) -> bool {
			u = root(root, u), v = root(root, v);
			if(u == v) return false;
			if(uf_data[u] > uf_data[v]) std::swap(u, v);
			uf_data[u] += uf_data[v];
			uf_data[v] = u;
			return true;
		};
		long long ret = 0;
		for(auto &e : Edges)
			if(unite(std::get<0>(e), std::get<1>(e))) ret += std::get<2>(e);
		return ret;
	}

	// O(V)
	std::vector<int> centroid() {
		int n = size();
		std::vector<int> centroid, sz(n);
		auto dfs = [&](auto self, int now, int per) -> void {
			sz[now] = 1;
			bool is_centroid = true;
			for(auto &e : edges[now]) {
				if(e.to != per) {
					self(self, e.to, now);
					sz[now] += sz[e.to];
					if(sz[e.to] > n / 2) is_centroid = false;
				}
			}
			if(n - sz[now] > n / 2) is_centroid = false;
			if(is_centroid) centroid.push_back(now);
		};
		dfs(dfs, 0, -1);
		return centroid;
	}

	// Ο(V+E)
	// directed graph from root to leaf
	Graph root_to_leaf(int root = 0) {
		Graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(now, e.to, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// Ο(V+E)
	// directed graph from leaf to root
	Graph leaf_to_root(int root = 0) {
		Graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(e.to, now, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// long long Chu_Liu_Edmonds(int root = 0) {}
};
#line 3 "/Users/nok0/Documents/Programming/nok0/graph/scc.hpp"

struct strongly_connected_components {
private:
	enum { CHECKED = -1,
		   UNCHECKED = -2 };
	const Graph &graph_given;
	Graph graph_reversed;
	std::vector<int> order, group_number; /* at the beginning of the building, 'group_number' is used as 'checked' */

	void dfs(int now) {
		if(group_number[now] != UNCHECKED) return;
		group_number[now] = CHECKED;
		for(auto &e : graph_given[now]) dfs(e.to);
		order.push_back(now);
	}

	void rdfs(int now, int group_count) {
		if(group_number[now] != UNCHECKED) return;
		group_number[now] = group_count;
		for(auto &e : graph_reversed[now]) rdfs(e.to, group_count);
	}

	void build(bool create_compressed_graph) {
		for(int i = 0; i < (int)graph_given.size(); i++) dfs(i);
		reverse(order.begin(), order.end());
		group_number.assign(graph_given.size(), UNCHECKED);
		int group = 0;
		for(auto &i : order)
			if(group_number[i] == UNCHECKED) rdfs(i, group), group++;
		graph_compressed.resize(group);
		groups.resize(group);
		for(int i = 0; i < (int)graph_given.size(); i++) groups[group_number[i]].push_back(i);
		if(create_compressed_graph) {
			std::vector<int> edges(group, -1);
			for(int i = 0; i < group; i++)
				for(auto &vertex : groups[i])
					for(auto &e : graph_given[vertex])
						if(group_number[e.to] != i and edges[group_number[e.to]] != i) {
							edges[group_number[e.to]] = i;
							graph_compressed[i].emplace_back(group_number[e.to], 1);
						}
		}
		return;
	}

public:
	std::vector<std::vector<int>> groups;
	Graph graph_compressed;

	strongly_connected_components(const Graph &g_, bool create_compressed_graph = false)
	  : graph_given(g_), graph_reversed(g_.size()), group_number(g_.size(), UNCHECKED) {
		for(size_t i = 0; i < g_.size(); i++)
			for(auto &e : graph_given[i]) graph_reversed[e.to].emplace_back(i, 1);
		build(create_compressed_graph);
	}

	const int &operator[](const int k) { return group_number[k]; }
};
#line 1 "/Users/nok0/Documents/Programming/nok0/template.hpp"
#include <bits/stdc++.h>
using namespace std;
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif

#pragma region Macros
// rep macro
#define foa(v, a) for(auto &v : a)
#define REPname(a, b, c, d, e, ...) e
#define REP(...) REPname(__VA_ARGS__, REP3, REP2, REP1, REP0)(__VA_ARGS__)
#define REP0(x) for(int i = 0; i < (x); ++i)
#define REP1(i, x) for(int i = 0; i < (x); ++i)
#define REP2(i, l, r) for(int i = (l); i < (r); ++i)
#define REP3(i, l, r, c) for(int i = (l); i < (r); i += (c))
#define REPSname(a, b, c, ...) c
#define REPS(...) REPSname(__VA_ARGS__, REPS1, REPS0)(__VA_ARGS__)
#define REPS0(x) for(int i = 1; i <= (x); ++i)
#define REPS1(i, x) for(int i = 1; i <= (x); ++i)
#define RREPname(a, b, c, d, e, ...) e
#define RREP(...) RREPname(__VA_ARGS__, RREP3, RREP2, RREP1, RREP0)(__VA_ARGS__)
#define RREP0(x) for(int i = (x)-1; i >= 0; --i)
#define RREP1(i, x) for(int i = (x)-1; i >= 0; --i)
#define RREP2(i, r, l) for(int i = (r)-1; i >= (l); --i)
#define RREP3(i, r, l, c) for(int i = (r)-1; i >= (l); i -= (c))
#define RREPSname(a, b, c, ...) c
#define RREPS(...) RREPSname(__VA_ARGS__, RREPS1, RREPS0)(__VA_ARGS__)
#define RREPS0(x) for(int i = (x); i >= 1; --i)
#define RREPS1(i, x) for(int i = (x); i >= 1; --i)

// name macro
#define pb push_back
#define eb emplace_back
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define popcnt(x) __builtin_popcountll(x)
template <class T = int>
using V = std::vector<T>;
template <class T = int>
using VV = std::vector<std::vector<T>>;
template <class T>
using pqup = std::priority_queue<T, std::vector<T>, std::greater<T>>;
using ll = long long;
using ld = long double;
using int128 = __int128_t;
using pii = std::pair<int, int>;
using pll = std::pair<long long, long long>;

// input macro
template <class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
	is >> p.first >> p.second;
	return is;
}
template <class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
	for(T &i : v) is >> i;
	return is;
}
std::istream &operator>>(std::istream &is, __int128_t &a) {
	std::string s;
	is >> s;
	__int128_t ret = 0;
	for(int i = 0; i < s.length(); i++)
		if('0' <= s[i] and s[i] <= '9')
			ret = 10 * ret + s[i] - '0';
	a = ret * (s[0] == '-' ? -1 : 1);
	return is;
}
#if __has_include(<atcoder/all>)
std::istream &operator>>(std::istream &is, atcoder::modint998244353 &a) {
	long long v;
	is >> v;
	a = v;
	return is;
}
std::istream &operator>>(std::istream &is, atcoder::modint1000000007 &a) {
	long long v;
	is >> v;
	a = v;
	return is;
}
template <int m>
std::istream &operator>>(std::istream &is, atcoder::static_modint<m> &a) {
	long long v;
	is >> v;
	a = v;
	return is;
}
template <int m>
std::istream &operator>>(std::istream &is, atcoder::dynamic_modint<m> &a) {
	long long v;
	is >> v;
	a = v;
	return is;
}
#endif
namespace scanner {
void scan(int &a) { std::cin >> a; }
void scan(long long &a) { std::cin >> a; }
void scan(std::string &a) { std::cin >> a; }
void scan(char &a) { std::cin >> a; }
void scan(char a[]) { std::scanf("%s", a); }
void scan(double &a) { std::cin >> a; }
void scan(long double &a) { std::cin >> a; }
template <class T, class U>
void scan(std::pair<T, U> &p) { std::cin >> p; }
template <class T>
void scan(std::vector<T> &a) { std::cin >> a; }
void INPUT() {}
template <class Head, class... Tail>
void INPUT(Head &head, Tail &... tail) {
	scan(head);
	INPUT(tail...);
}
}  // namespace scanner
#define VEC(type, name, size)     \
	std::vector<type> name(size); \
	scanner::INPUT(name)
#define VVEC(type, name, h, w)                                    \
	std::vector<std::vector<type>> name(h, std::vector<type>(w)); \
	scanner::INPUT(name)
#define INT(...)     \
	int __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LL(...)            \
	long long __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define STR(...)             \
	std::string __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define CHAR(...)     \
	char __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define DOUBLE(...)     \
	double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LD(...)              \
	long double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)

// output-macro
template <class T, class U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
	os << p.first << " " << p.second;
	return os;
}
template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &a) {
	for(int i = 0; i < int(a.size()); ++i) {
		if(i) os << " ";
		os << a[i];
	}
	return os;
}
std::ostream &operator<<(std::ostream &dest, __int128_t &value) {
	std::ostream::sentry s(dest);
	if(s) {
		__uint128_t tmp = value < 0 ? -value : value;
		char buffer[128];
		char *d = std::end(buffer);
		do {
			--d;
			*d = "0123456789"[tmp % 10];
			tmp /= 10;
		} while(tmp != 0);
		if(value < 0) {
			--d;
			*d = '-';
		}
		int len = std::end(buffer) - d;
		if(dest.rdbuf()->sputn(d, len) != len) {
			dest.setstate(std::ios_base::badbit);
		}
	}
	return dest;
}
#if __has_include(<atcoder/all>)
std::ostream &operator<<(std::ostream &os, const atcoder::modint998244353 &a) { return os << a.val(); }
std::ostream &operator<<(std::ostream &os, const atcoder::modint1000000007 &a) { return os << a.val(); }
template <int m>
std::ostream &operator<<(std::ostream &os, const atcoder::static_modint<m> &a) { return os << a.val(); }
template <int m>
std::ostream &operator<<(std::ostream &os, const atcoder::dynamic_modint<m> &a) { return os << a.val(); }
#endif
template <class T>
void print(const T a) { std::cout << a << '\n'; }
template <class Head, class... Tail>
void print(Head H, Tail... T) {
	std::cout << H << ' ';
	print(T...);
}
template <class T>
void printel(const T a) { std::cout << a << '\n'; }
template <class T>
void printel(const std::vector<T> &a) {
	for(const auto &v : a)
		std::cout << v << '\n';
}
template <class Head, class... Tail>
void printel(Head H, Tail... T) {
	std::cout << H << '\n';
	printel(T...);
}
void Yes(const bool b = true) { std::cout << (b ? "Yes\n" : "No\n"); }
void No() { std::cout << "No\n"; }
void YES(const bool b = true) { std::cout << (b ? "YES\n" : "NO\n"); }
void NO() { std::cout << "NO\n"; }
void err(const bool b = true) {
	if(b) {
		std::cout << "-1\n", exit(0);
	}
}

//debug macro
namespace debugger {
template <class T>
void view(const std::vector<T> &a) {
	std::cerr << "{ ";
	for(const auto &v : a) {
		std::cerr << v << ", ";
	}
	std::cerr << "\b\b }";
}
template <class T>
void view(const std::vector<std::vector<T>> &a) {
	std::cerr << "{\n";
	for(const auto &v : a) {
		std::cerr << "\t";
		view(v);
		std::cerr << "\n";
	}
	std::cerr << "}";
}
template <class T, class U>
void view(const std::vector<std::pair<T, U>> &a) {
	std::cerr << "{\n";
	for(const auto &p : a) std::cerr << "\t(" << p.first << ", " << p.second << ")\n";
	std::cerr << "}";
}
template <class T, class U>
void view(const std::map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &p : m) std::cerr << "\t[" << p.first << "] : " << p.second << "\n";
	std::cerr << "}";
}
template <class T, class U>
void view(const std::pair<T, U> &p) { std::cerr << "(" << p.first << ", " << p.second << ")"; }
template <class T>
void view(const std::set<T> &s) {
	std::cerr << "{ ";
	for(auto &v : s) {
		view(v);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <class T>
void view(const T &e) { std::cerr << e; }
}  // namespace debugger
#ifdef LOCAL
void debug_out() {}
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	debugger::view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "\b\b]\n";                                   \
	} while(false)
#else
#define debug(...) (void(0))
#endif

// vector macro
template <class T>
int lb(const std::vector<T> &a, const T x) { return std::distance((a).begin(), std::lower_bound((a).begin(), (a).end(), (x))); }
template <class T>
int ub(const std::vector<T> &a, const T x) { return std::distance((a).begin(), std::upper_bound((a).begin(), (a).end(), (x))); }
template <class T>
void UNIQUE(std::vector<T> &a) {
	std::sort(a.begin(), a.end());
	a.erase(std::unique(a.begin(), a.end()), a.end());
}
template <class T>
std::vector<T> press(std::vector<T> &a) {
	auto res = a;
	UNIQUE(res);
	for(auto &v : a)
		v = lb(res, v);
	return res;
}
#define SORTname(a, b, c, ...) c
#define SORT(...) SORTname(__VA_ARGS__, SORT1, SORT0, ...)(__VA_ARGS__)
#define SORT0(a) std::sort((a).begin(), (a).end())
#define SORT1(a, c) std::sort((a).begin(), (a).end(), [](const auto x, const auto y) { return x c y; })
template <class T>
void ADD(std::vector<T> &a, const T x = 1) {
	for(auto &v : a) v += x;
}
template <class T>
void SUB(std::vector<T> &a, const T x = 1) {
	for(auto &v : a) v -= x;
}
template <class T>
void MUL(std::vector<T> &a, const T x) {
	for(auto &v : a) v *= x;
}
template <class T>
void DIV(std::vector<T> &a, const T x) {
	for(auto &v : a) v /= x;
}
std::vector<std::pair<char, int>> rle(const string &s) {
	int n = s.size();
	std::vector<std::pair<char, int>> ret;
	for(int l = 0; l < n;) {
		int r = l + 1;
		for(; r < n and s[l] == s[r]; r++) {}
		ret.emplace_back(s[l], r - l);
		l = r;
	}
	return ret;
}
template <class T>
std::vector<std::pair<T, int>> rle(const std::vector<T> &v) {
	int n = v.size();
	std::vector<std::pair<T, int>> ret;
	for(int l = 0; l < n;) {
		int r = l + 1;
		for(; r < n and v[l] == v[r]; r++) {}
		ret.emplace_back(v[l], r - l);
		l = r;
	}
	return ret;
}

// math macro
template <class T, class U>
inline bool chmin(T &a, const U &b) { return a > b ? a = b, true : false; }
template <class T, class U>
inline bool chmax(T &a, const U &b) { return a < b ? a = b, true : false; }
template <class T>
T divup(T x, T y) { return (x + y - 1) / y; }
template <class T>
T POW(T a, long long n) {
	T ret = 1;
	while(n) {
		if(n & 1) ret *= a;
		a *= a;
		n >>= 1;
	}
	return ret;
}
// modpow
long long POW(long long a, long long n, const int mod) {
	long long ret = 1;
	a = (a % mod + mod) % mod;
	while(n) {
		if(n & 1) (ret *= a) %= mod;
		(a *= a) %= mod;
		n >>= 1;
	}
	return ret;
}

// others
struct fast_io {
	fast_io() {
		ios::sync_with_stdio(false);
		cin.tie(nullptr);
		cout << fixed << setprecision(15);
	}
} fast_io_;
const int inf = 1e9;
const ll INF = 1e18;
#pragma endregion

void main_();

int main() {
	main_();
	return 0;
}
#line 4 "h.cpp"

const int MAX_LEN_P = 11;
void main_() {
	//input
	INT(n, m);
	VEC(string, s, n);
	vector adj(n, vector(n, false));
	REP(i, m) {
		INT(x, y);
		adj[--x][--y] = true;
	}

	int N = n * n * MAX_LEN_P * 2;
	Graph g(N);
	//一番左がword l , 一番右がword r, 左がlen文字余ってる
	auto left_id = [&](int l, int r, int len) {
		return l * n * MAX_LEN_P + r * MAX_LEN_P + len;
	};
	auto right_id = [&](int l, int r, int len) {
		return n * n * MAX_LEN_P + l * n * MAX_LEN_P + r * MAX_LEN_P + len;
	};

	map<pii, int> mp;

	//aaba -> aabaabc
	//左が余っている場合->右を追加する
	REP(l, n) {
		REP(len, SZ(s[l]) + 1) {
			auto amari = s[l].substr(0, len);
			reverse(all(amari));
			REP(r, n) {
				REP(len2, SZ(s[r]) + 1) {
					auto kesu = s[r].substr(0, len2);
					if(amari != kesu) continue;
					REP(r2, n) {
						if(!adj[r2][r]) continue;
						int u = left_id(l, r2, len);
						int v = right_id(l, r, SZ(s[r]) - len2);
						g.add_edge(u, v, SZ(s[r]), 1);
						chmax(mp[{u, v}], SZ(s[r]));
					}
				}
			}
		}
	}

	//右が余っている場合
	REP(r, n) {
		REP(len, SZ(s[r]) + 1) {
			auto amari = s[r].substr(SZ(s[r]) - len);
			reverse(all(amari));
			REP(l, n) {
				REP(len2, SZ(s[l]) + 1) {
					auto kesu = s[l].substr(SZ(s[l]) - len2);
					if(amari != kesu) continue;
					REP(l2, n) {
						if(!adj[l][l2]) continue;
						int u = right_id(l2, r, len);
						int v = left_id(l, r, SZ(s[l]) - len2);
						g.add_edge(u, v, SZ(s[l]), 1);
						chmax(mp[{u, v}], SZ(s[l]));
					}
				}
			}
		}
	}

	//左が余ってる->右追加する->やっぱり左余る
	REP(l, n) {
		REP(len, SZ(s[l]) + 1) {
			REP(r, n) {
				if(len < SZ(s[r])) continue;
				int len2 = SZ(s[r]);
				string lef = s[l].substr(len - len2, len2);
				reverse(all(lef));
				if(lef == s[r]) {
					REP(r2, n) {
						if(!adj[r2][r]) continue;
						int u = left_id(l, r2, len);
						int v = left_id(l, r, len - len2);
						g.add_edge(u, v, SZ(s[r]), 1);
						chmax(mp[{u, v}], SZ(s[r]));
					}
				}
			}
		}
	}

	//右が余ってる->左追加する->やっぱり右余る
	REP(r, n) {
		REP(len, SZ(s[r]) + 1) {
			REP(l, n) {
				if(len < SZ(s[l])) continue;
				int len2 = SZ(s[l]);
				string rig = s[r].substr(SZ(s[r]) - len, len2);
				reverse(all(rig));
				if(rig == s[l]) {
					REP(l2, n) {
						if(!adj[l][l2]) continue;
						int u = right_id(l2, r, len);
						int v = right_id(l, r, len - len2);
						g.add_edge(u, v, SZ(s[l]), 1);
						chmax(mp[{u, v}], SZ(s[l]));
					}
				}
			}
		}
	}

	strongly_connected_components scc(g, 1);
	auto &h = scc.graph_compressed;

	N = SZ(h);

	vector dp(N, -inf);
	vector vis(N, false);

	REP(i, n) {
		//左を余らせる場合
		REP(len, SZ(s[i]) + 1) {
			string lef = s[i].substr(len);
			string rig = lef;
			reverse(all(rig));
			if(lef == rig) {
				dp[scc[left_id(i, i, len)]] = SZ(s[i]);
				vis[scc[left_id(i, i, len)]] = 1;
			}
		}

		//右を余らせる場合
		REP(len, SZ(s[i]) + 1) {
			string lef = s[i].substr(0, SZ(s[i]) - len);
			string rig = lef;
			reverse(all(rig));
			if(lef == rig) {
				dp[scc[right_id(i, i, len)]] = SZ(s[i]);
				vis[scc[right_id(i, i, len)]] = 1;
			}
		}
	}

	auto &groups = scc.groups;
	//debug(groups);

	REP(i, N) {
		if(SZ(groups[i]) != 1) dp[i] = inf;
	}

	auto tsort = h.topological_sort();

	foa(idx, tsort) {
		if(!vis[idx]) continue;
		for(auto [to, cost] : h[idx]) {
			vis[to] = true;
			chmax(dp[to], dp[idx] + mp[{groups[idx][0], groups[to][0]}]);
		}
	}

	int res = 0;
	REP(i, n) {
		REP(j, n) {
			int lid = scc[left_id(i, j, 0)];
			if(vis[lid]) {
				chmax(res, dp[lid]);
			}
			int rid = scc[right_id(i, j, 0)];
			if(vis[rid]) {
				chmax(res, dp[rid]);
			}
		}
	}

	print(res >= inf ? -1 : res);
}
accuracy: 1
time_ms: 190
memory_kb: 87896
final_rank: 10

code_snippet: #line 2 "/Users/nok0/Documents/Programming/nok0/graph/graph.hpp"
#include <algorithm>
#include <cassert>
#include <deque>
#include <iostream>
#include <queue>
#include <tuple>
#include <utility>
#include <vector>

struct Edge {
	int to;
	long long cost;
	Edge() = default;
	Edge(int to_, long long cost_) : to(to_), cost(cost_) {}
	bool operator<(const Edge &a) const { return cost < a.cost; }
	bool operator>(const Edge &a) const { return cost > a.cost; }
	friend std::ostream &operator<<(std::ostream &s, Edge &a) {
		s << "to: " << a.to << ", cost: " << a.cost;
		return s;
	}
};

class Graph {
	std::vector<std::vector<Edge>> edges;

	template <class F>
	struct rec_lambda {
		F f;
		rec_lambda(F &&f_) : f(std::forward<F>(f_)) {}
		template <class... Args>
		auto operator()(Args &&... args) const {
			return f(*this, std::forward<Args>(args)...);
		}
	};

public:
	inline const std::vector<Edge> &operator[](int k) const { return edges[k]; }
	inline std::vector<Edge> &operator[](int k) { return edges[k]; }

	int size() const { return edges.size(); }
	void resize(const int n) { edges.resize(n); }

	Graph() = default;
	Graph(int n) : edges(n) {}
	Graph(int n, int e, bool weight = 0, bool directed = 0, int idx = 1) : edges(n) { input(e, weight, directed, idx); }
	const long long INF = 3e18;

	void input(int e = -1, bool weight = 0, bool directed = false, int idx = 1) {
		if(e == -1) e = size() - 1;
		while(e--) {
			int u, v;
			long long cost = 1;
			std::cin >> u >> v;
			if(weight) std::cin >> cost;
			u -= idx, v -= idx;
			edges[u].emplace_back(v, cost);
			if(!directed) edges[v].emplace_back(u, cost);
		}
	}

	void add_edge(int u, int v, long long cost = 1, bool directed = false, int idx = 0) {
		u -= idx, v -= idx;
		edges[u].emplace_back(v, cost);
		if(!directed) edges[v].emplace_back(u, cost);
	}

	// Ο(V+E)
	std::vector<long long> bfs(int s) {
		std::vector<long long> dist(size(), INF);
		std::queue<int> que;
		dist[s] = 0;
		que.push(s);
		while(!que.empty()) {
			int v = que.front();
			que.pop();
			for(auto &e : edges[v]) {
				if(dist[e.to] != INF) continue;
				dist[e.to] = dist[v] + e.cost;
				que.push(e.to);
			}
		}
		return dist;
	}

	// Ο(V+E)
	// constraint: cost of each edge is zero or one
	std::vector<long long> zero_one_bfs(int s) {
		std::vector<long long> dist(size(), INF);
		std::deque<int> deq;
		dist[s] = 0;
		deq.push_back(s);
		while(!deq.empty()) {
			int v = deq.front();
			deq.pop_front();
			for(auto &e : edges[v]) {
				assert(0LL <= e.cost and e.cost < 2LL);
				if(e.cost and dist[e.to] > dist[v] + 1) {
					dist[e.to] = dist[v] + 1;
					deq.push_back(e.to);
				} else if(!e.cost and dist[e.to] > dist[v]) {
					dist[e.to] = dist[v];
					deq.push_front(e.to);
				}
			}
		}
		return dist;
	}

	// Ο((E+V)logV)
	// cannot reach: INF
	std::vector<long long> dijkstra(int s) {  // verified
		std::vector<long long> dist(size(), INF);
		const auto compare = [](const std::pair<long long, int> &a, const std::pair<long long, int> &b) { return a.first > b.first; };
		std::priority_queue<std::pair<long long, int>, std::vector<std::pair<long long, int>>, decltype(compare)> que{compare};
		dist[s] = 0;
		que.emplace(0, s);
		while(!que.empty()) {
			std::pair<long long, int> p = que.top();
			que.pop();
			int v = p.second;
			if(dist[v] < p.first) continue;
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					que.emplace(dist[e.to], e.to);
				}
			}
		}
		return dist;
	}

	// Ο(VE)
	// cannot reach: INF
	// negative cycle: -INF
	std::vector<long long> bellman_ford(int s) {  // verified
		int n = size();
		std::vector<long long> res(n, INF);
		res[s] = 0;
		for(int loop = 0; loop < n - 1; loop++) {
			for(int v = 0; v < n; v++) {
				if(res[v] == INF) continue;
				for(auto &e : edges[v]) {
					res[e.to] = std::min(res[e.to], res[v] + e.cost);
				}
			}
		}
		std::queue<int> que;
		std::vector<int> chk(n);
		for(int v = 0; v < n; v++) {
			if(res[v] == INF) continue;
			for(auto &e : edges[v]) {
				if(res[e.to] > res[v] + e.cost and !chk[e.to]) {
					que.push(e.to);
					chk[e.to] = 1;
				}
			}
		}
		while(!que.empty()) {
			int now = que.front();
			que.pop();
			for(auto &e : edges[now]) {
				if(!chk[e.to]) {
					chk[e.to] = 1;
					que.push(e.to);
				}
			}
		}
		for(int i = 0; i < n; i++)
			if(chk[i]) res[i] = -INF;
		return res;
	}

	// Ο(V^3)
	std::vector<std::vector<long long>> warshall_floyd() {  // verified
		int n = size();
		std::vector<std::vector<long long>> dist(n, std::vector<long long>(n, INF));
		for(int i = 0; i < n; i++) dist[i][i] = 0;
		for(int i = 0; i < n; i++)
			for(auto &e : edges[i]) dist[i][e.to] = std::min(dist[i][e.to], e.cost);
		for(int k = 0; k < n; k++)
			for(int i = 0; i < n; i++) {
				if(dist[i][k] == INF) continue;
				for(int j = 0; j < n; j++) {
					if(dist[k][j] == INF) continue;
					dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
				}
			}
		return dist;
	}

	// Ο(V) (using DFS)
	// if a directed cycle exists, return {}
	std::vector<int> topological_sort() {  // verified
		std::vector<int> res;
		int n = size();
		std::vector<int> used(n, 0);
		bool not_DAG = false;
		for(int i = 0; i < n; i++) {
			rec_lambda([&](auto &&dfs, int k) -> void {
				if(not_DAG) return;
				if(used[k]) {
					if(used[k] == 1) not_DAG = true;
					return;
				}
				used[k] = 1;
				for(auto &e : edges[k]) dfs(e.to);
				used[k] = 2;
				res.push_back(k);
			})(i);
		}
		if(not_DAG) return std::vector<int>{};
		std::reverse(res.begin(), res.end());
		return res;
	}

	bool is_DAG() { return !topological_sort().empty(); }  // verified

	// Ο(V)
	// array of the distance from each vertex to the most distant vertex
	std::vector<long long> height() {  // verified
		auto vec1 = bfs(0);
		int v1 = -1, v2 = -1;
		long long dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v1 = i;
		vec1 = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v2 = i;
		auto vec2 = bfs(v2);
		for(int i = 0; i < int(size()); i++)
			if(vec1[i] < vec2[i]) vec1[i] = vec2[i];
		return vec1;
	}

	// O(V+E)
	// vector<(int)(0 or 1)>
	// if it is not bipartite, return {}
	std::vector<int> bipartite_grouping() {
		std::vector<int> colors(size(), -1);
		auto dfs = [&](auto self, int now, int col) -> bool {
			colors[now] = col;
			for(auto &e : edges[now]) {
				if(col == colors[e.to]) return false;
				if(colors[e.to] == -1 and !self(self, e.to, !col)) return false;
			}
			return true;
		};
		for(int i = 0; i < int(size()); i++)
			if(colors[i] == -1 and !dfs(dfs, i, 0)) return std::vector<int>{};
		return colors;
	}

	bool is_bipartite() { return !bipartite_grouping().empty(); }

	// Ο(V+E)
	// ((v1, v2), diameter)
	std::pair<std::pair<int, int>, long long> diameter() {  // verified
		auto vec = bfs(0);
		int v1 = -1, v2 = -1;
		long long dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec[i]) dia = vec[i], v1 = i;
		vec = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec[i]) dia = vec[i], v2 = i;
		std::pair<std::pair<int, int>, long long> res = {{v1, v2}, dia};
		return res;
	}

	// Ο(V+E)
	// return {s, v1, v2, ... t}
	std::vector<int> diameter_path() {
		auto vec = bfs(0);
		int v1 = -1, v2 = -1;
		long long dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec[i]) dia = vec[i], v1 = i;
		auto vec2 = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec2[i]) dia = vec2[i], v2 = i;
		vec = bfs(v2);
		std::vector<int> ret;
		auto dfs = [&](auto self, int now, int p) -> void {
			ret.emplace_back(now);
			if(now == v2) return;
			for(auto &[to, cost] : (*this)[now]) {
				if(vec[to] + vec2[to] == dia and to != p)
					self(self, to, now);
			}
		};
		dfs(dfs, v1, -1);
		return ret;
	}

	// Ο(V+E)
	// return subtree_size, root = root
	std::vector<int> subtree_size(const int root) {
		int n = size();
		std::vector<int> ret(n, 1);
		rec_lambda([&](auto &&dfs, int now, int p) -> void {
			for(auto &[to, cost] : (*this)[now]) {
				if(to == p) continue;
				dfs(to, now);
				ret[now] += ret[to];
			}
		})(root, -1);
		return ret;
	}

	// Ο(ElogV)
	long long prim() {  // verified
		long long res = 0;
		std::priority_queue<Edge, std::vector<Edge>, std::greater<Edge>> que;
		for(auto &e : edges[0]) que.push(e);
		std::vector<int> chk(size());
		chk[0] = 1;
		int cnt = 1;
		while(cnt < size()) {
			auto e = que.top();
			que.pop();
			if(chk[e.to]) continue;
			cnt++;
			res += e.cost;
			chk[e.to] = 1;
			for(auto &e2 : edges[e.to]) que.push(e2);
		}
		return res;
	}

	// Ο(ElogE)
	long long kruskal() {  // verified
		std::vector<std::tuple<int, int, long long>> Edges;
		for(int i = 0; i < int(size()); i++)
			for(auto &e : edges[i]) Edges.emplace_back(i, e.to, e.cost);
		std::sort(Edges.begin(), Edges.end(), [](const std::tuple<int, int, long long> &a, const std::tuple<int, int, long long> &b) {
			return std::get<2>(a) < std::get<2>(b);
		});
		std::vector<int> uf_data(size(), -1);
		auto root = [&uf_data](auto self, int x) -> int {
			if(uf_data[x] < 0) return x;
			return uf_data[x] = self(self, uf_data[x]);
		};
		auto unite = [&uf_data, &root](int u, int v) -> bool {
			u = root(root, u), v = root(root, v);
			if(u == v) return false;
			if(uf_data[u] > uf_data[v]) std::swap(u, v);
			uf_data[u] += uf_data[v];
			uf_data[v] = u;
			return true;
		};
		long long ret = 0;
		for(auto &e : Edges)
			if(unite(std::get<0>(e), std::get<1>(e))) ret += std::get<2>(e);
		return ret;
	}

	// O(V)
	std::vector<int> centroid() {
		int n = size();
		std::vector<int> centroid, sz(n);
		auto dfs = [&](auto self, int now, int per) -> void {
			sz[now] = 1;
			bool is_centroid = true;
			for(auto &e : edges[now]) {
				if(e.to != per) {
					self(self, e.to, now);
					sz[now] += sz[e.to];
					if(sz[e.to] > n / 2) is_centroid = false;
				}
			}
			if(n - sz[now] > n / 2) is_centroid = false;
			if(is_centroid) centroid.push_back(now);
		};
		dfs(dfs, 0, -1);
		return centroid;
	}

	// Ο(V+E)
	// directed graph from root to leaf
	Graph root_to_leaf(int root = 0) {
		Graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(now, e.to, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// Ο(V+E)
	// directed graph from leaf to root
	Graph leaf_to_root(int root = 0) {
		Graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(e.to, now, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// long long Chu_Liu_Edmonds(int root = 0) {}
};
#line 3 "/Users/nok0/Documents/Programming/nok0/graph/scc.hpp"

struct strongly_connected_components {
private:
	enum { CHECKED = -1,
		   UNCHECKED = -2 };
	const Graph &graph_given;
	Graph graph_reversed;
	std::vector<int> order, group_number; /* at the beginning of the building, 'group_number' is used as 'checked' */

	void dfs(int now) {
		if(group_number[now] != UNCHECKED) return;
		group_number[now] = CHECKED;
		for(auto &e : graph_given[now]) dfs(e.to);
		order.push_back(now);
	}

	void rdfs(int now, int group_count) {
		if(group_number[now] != UNCHECKED) return;
		group_number[now] = group_count;
		for(auto &e : graph_reversed[now]) rdfs(e.to, group_count);
	}

	void build(bool create_compressed_graph) {
		for(int i = 0; i < (int)graph_given.size(); i++) dfs(i);
		reverse(order.begin(), order.end());
		group_number.assign(graph_given.size(), UNCHECKED);
		int group = 0;
		for(auto &i : order)
			if(group_number[i] == UNCHECKED) rdfs(i, group), group++;
		graph_compressed.resize(group);
		groups.resize(group);
		for(int i = 0; i < (int)graph_given.size(); i++) groups[group_number[i]].push_back(i);
		if(create_compressed_graph) {
			std::vector<int> edges(group, -1);
			for(int i = 0; i < group; i++)
				for(auto &vertex : groups[i])
					for(auto &e : graph_given[vertex])
						if(group_number[e.to] != i and edges[group_number[e.to]] != i) {
							edges[group_number[e.to]] = i;
							graph_compressed[i].emplace_back(group_number[e.to], 1);
						}
		}
		return;
	}

public:
	std::vector<std::vector<int>> groups;
	Graph graph_compressed;

	strongly_connected_components(const Graph &g_, bool create_compressed_graph = false)
	  : graph_given(g_), graph_reversed(g_.size()), group_number(g_.size(), UNCHECKED) {
		for(size_t i = 0; i < g_.size(); i++)
			for(auto &e : graph_given[i]) graph_reversed[e.to].emplace_back(i, 1);
		build(create_compressed_graph);
	}

	const int &operator[](const int k) { return group_number[k]; }
};
#line 1 "/Users/nok0/Documents/Programming/nok0/template.hpp"
#include <bits/stdc++.h>
using namespace std;
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif

#pragma region Macros
// rep macro
#define foa(v, a) for(auto &v : a)
#define REPname(a, b, c, d, e, ...) e
#define REP(...) REPname(__VA_ARGS__, REP3, REP2, REP1, REP0)(__VA_ARGS__)
#define REP0(x) for(int i = 0; i < (x); ++i)
#define REP1(i, x) for(int i = 0; i < (x); ++i)
#define REP2(i, l, r) for(int i = (l); i < (r); ++i)
#define REP3(i, l, r, c) for(int i = (l); i < (r); i += (c))
#define REPSname(a, b, c, ...) c
#define REPS(...) REPSname(__VA_ARGS__, REPS1, REPS0)(__VA_ARGS__)
#define REPS0(x) for(int i = 1; i <= (x); ++i)
#define REPS1(i, x) for(int i = 1; i <= (x); ++i)
#define RREPname(a, b, c, d, e, ...) e
#define RREP(...) RREPname(__VA_ARGS__, RREP3, RREP2, RREP1, RREP0)(__VA_ARGS__)
#define RREP0(x) for(int i = (x)-1; i >= 0; --i)
#define RREP1(i, x) for(int i = (x)-1; i >= 0; --i)
#define RREP2(i, r, l) for(int i = (r)-1; i >= (l); --i)
#define RREP3(i, r, l, c) for(int i = (r)-1; i >= (l); i -= (c))
#define RREPSname(a, b, c, ...) c
#define RREPS(...) RREPSname(__VA_ARGS__, RREPS1, RREPS0)(__VA_ARGS__)
#define RREPS0(x) for(int i = (x); i >= 1; --i)
#define RREPS1(i, x) for(int i = (x); i >= 1; --i)

// name macro
#define pb push_back
#define eb emplace_back
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define popcnt(x) __builtin_popcountll(x)
template <class T = int>
using V = std::vector<T>;
template <class T = int>
using VV = std::vector<std::vector<T>>;
template <class T>
using pqup = std::priority_queue<T, std::vector<T>, std::greater<T>>;
using ll = long long;
using ld = long double;
using int128 = __int128_t;
using pii = std::pair<int, int>;
using pll = std::pair<long long, long long>;

// input macro
template <class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
	is >> p.first >> p.second;
	return is;
}
template <class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
	for(T &i : v) is >> i;
	return is;
}
std::istream &operator>>(std::istream &is, __int128_t &a) {
	std::string s;
	is >> s;
	__int128_t ret = 0;
	for(int i = 0; i < s.length(); i++)
		if('0' <= s[i] and s[i] <= '9')
			ret = 10 * ret + s[i] - '0';
	a = ret * (s[0] == '-' ? -1 : 1);
	return is;
}
#if __has_include(<atcoder/all>)
std::istream &operator>>(std::istream &is, atcoder::modint998244353 &a) {
	long long v;
	is >> v;
	a = v;
	return is;
}
std::istream &operator>>(std::istream &is, atcoder::modint1000000007 &a) {
	long long v;
	is >> v;
	a = v;
	return is;
}
template <int m>
std::istream &operator>>(std::istream &is, atcoder::static_modint<m> &a) {
	long long v;
	is >> v;
	a = v;
	return is;
}
template <int m>
std::istream &operator>>(std::istream &is, atcoder::dynamic_modint<m> &a) {
	long long v;
	is >> v;
	a = v;
	return is;
}
#endif
namespace scanner {
void scan(int &a) { std::cin >> a; }
void scan(long long &a) { std::cin >> a; }
void scan(std::string &a) { std::cin >> a; }
void scan(char &a) { std::cin >> a; }
void scan(char a[]) { std::scanf("%s", a); }
void scan(double &a) { std::cin >> a; }
void scan(long double &a) { std::cin >> a; }
template <class T, class U>
void scan(std::pair<T, U> &p) { std::cin >> p; }
template <class T>
void scan(std::vector<T> &a) { std::cin >> a; }
void INPUT() {}
template <class Head, class... Tail>
void INPUT(Head &head, Tail &... tail) {
	scan(head);
	INPUT(tail...);
}
}  // namespace scanner
#define VEC(type, name, size)     \
	std::vector<type> name(size); \
	scanner::INPUT(name)
#define VVEC(type, name, h, w)                                    \
	std::vector<std::vector<type>> name(h, std::vector<type>(w)); \
	scanner::INPUT(name)
#define INT(...)     \
	int __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LL(...)            \
	long long __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define STR(...)             \
	std::string __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define CHAR(...)     \
	char __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define DOUBLE(...)     \
	double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LD(...)              \
	long double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)

// output-macro
template <class T, class U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
	os << p.first << " " << p.second;
	return os;
}
template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &a) {
	for(int i = 0; i < int(a.size()); ++i) {
		if(i) os << " ";
		os << a[i];
	}
	return os;
}
std::ostream &operator<<(std::ostream &dest, __int128_t &value) {
	std::ostream::sentry s(dest);
	if(s) {
		__uint128_t tmp = value < 0 ? -value : value;
		char buffer[128];
		char *d = std::end(buffer);
		do {
			--d;
			*d = "0123456789"[tmp % 10];
			tmp /= 10;
		} while(tmp != 0);
		if(value < 0) {
			--d;
			*d = '-';
		}
		int len = std::end(buffer) - d;
		if(dest.rdbuf()->sputn(d, len) != len) {
			dest.setstate(std::ios_base::badbit);
		}
	}
	return dest;
}
#if __has_include(<atcoder/all>)
std::ostream &operator<<(std::ostream &os, const atcoder::modint998244353 &a) { return os << a.val(); }
std::ostream &operator<<(std::ostream &os, const atcoder::modint1000000007 &a) { return os << a.val(); }
template <int m>
std::ostream &operator<<(std::ostream &os, const atcoder::static_modint<m> &a) { return os << a.val(); }
template <int m>
std::ostream &operator<<(std::ostream &os, const atcoder::dynamic_modint<m> &a) { return os << a.val(); }
#endif
template <class T>
void print(const T a) { std::cout << a << '\n'; }
template <class Head, class... Tail>
void print(Head H, Tail... T) {
	std::cout << H << ' ';
	print(T...);
}
template <class T>
void printel(const T a) { std::cout << a << '\n'; }
template <class T>
void printel(const std::vector<T> &a) {
	for(const auto &v : a)
		std::cout << v << '\n';
}
template <class Head, class... Tail>
void printel(Head H, Tail... T) {
	std::cout << H << '\n';
	printel(T...);
}
void Yes(const bool b = true) { std::cout << (b ? "Yes\n" : "No\n"); }
void No() { std::cout << "No\n"; }
void YES(const bool b = true) { std::cout << (b ? "YES\n" : "NO\n"); }
void NO() { std::cout << "NO\n"; }
void err(const bool b = true) {
	if(b) {
		std::cout << "-1\n", exit(0);
	}
}

//debug macro
namespace debugger {
template <class T>
void view(const std::vector<T> &a) {
	std::cerr << "{ ";
	for(const auto &v : a) {
		std::cerr << v << ", ";
	}
	std::cerr << "\b\b }";
}
template <class T>
void view(const std::vector<std::vector<T>> &a) {
	std::cerr << "{\n";
	for(const auto &v : a) {
		std::cerr << "\t";
		view(v);
		std::cerr << "\n";
	}
	std::cerr << "}";
}
template <class T, class U>
void view(const std::vector<std::pair<T, U>> &a) {
	std::cerr << "{\n";
	for(const auto &p : a) std::cerr << "\t(" << p.first << ", " << p.second << ")\n";
	std::cerr << "}";
}
template <class T, class U>
void view(const std::map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &p : m) std::cerr << "\t[" << p.first << "] : " << p.second << "\n";
	std::cerr << "}";
}
template <class T, class U>
void view(const std::pair<T, U> &p) { std::cerr << "(" << p.first << ", " << p.second << ")"; }
template <class T>
void view(const std::set<T> &s) {
	std::cerr << "{ ";
	for(auto &v : s) {
		view(v);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <class T>
void view(const T &e) { std::cerr << e; }
}  // namespace debugger
#ifdef LOCAL
void debug_out() {}
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	debugger::view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "\b\b]\n";                                   \
	} while(false)
#else
#define debug(...) (void(0))
#endif

// vector macro
template <class T>
int lb(const std::vector<T> &a, const T x) { return std::distance((a).begin(), std::lower_bound((a).begin(), (a).end(), (x))); }
template <class T>
int ub(const std::vector<T> &a, const T x) { return std::distance((a).begin(), std::upper_bound((a).begin(), (a).end(), (x))); }
template <class T>
void UNIQUE(std::vector<T> &a) {
	std::sort(a.begin(), a.end());
	a.erase(std::unique(a.begin(), a.end()), a.end());
}
template <class T>
std::vector<T> press(std::vector<T> &a) {
	auto res = a;
	UNIQUE(res);
	for(auto &v : a)
		v = lb(res, v);
	return res;
}
#define SORTname(a, b, c, ...) c
#define SORT(...) SORTname(__VA_ARGS__, SORT1, SORT0, ...)(__VA_ARGS__)
#define SORT0(a) std::sort((a).begin(), (a).end())
#define SORT1(a, c) std::sort((a).begin(), (a).end(), [](const auto x, const auto y) { return x c y; })
template <class T>
void ADD(std::vector<T> &a, const T x = 1) {
	for(auto &v : a) v += x;
}
template <class T>
void SUB(std::vector<T> &a, const T x = 1) {
	for(auto &v : a) v -= x;
}
template <class T>
void MUL(std::vector<T> &a, const T x) {
	for(auto &v : a) v *= x;
}
template <class T>
void DIV(std::vector<T> &a, const T x) {
	for(auto &v : a) v /= x;
}
std::vector<std::pair<char, int>> rle(const string &s) {
	int n = s.size();
	std::vector<std::pair<char, int>> ret;
	for(int l = 0; l < n;) {
		int r = l + 1;
		for(; r < n and s[l] == s[r]; r++) {}
		ret.emplace_back(s[l], r - l);
		l = r;
	}
	return ret;
}
template <class T>
std::vector<std::pair<T, int>> rle(const std::vector<T> &v) {
	int n = v.size();
	std::vector<std::pair<T, int>> ret;
	for(int l = 0; l < n;) {
		int r = l + 1;
		for(; r < n and v[l] == v[r]; r++) {}
		ret.emplace_back(v[l], r - l);
		l = r;
	}
	return ret;
}

// math macro
template <class T, class U>
inline bool chmin(T &a, const U &b) { return a > b ? a = b, true : false; }
template <class T, class U>
inline bool chmax(T &a, const U &b) { return a < b ? a = b, true : false; }
template <class T>
T divup(T x, T y) { return (x + y - 1) / y; }
template <class T>
T POW(T a, long long n) {
	T ret = 1;
	while(n) {
		if(n & 1) ret *= a;
		a *= a;
		n >>= 1;
	}
	return ret;
}
// modpow
long long POW(long long a, long long n, const int mod) {
	long long ret = 1;
	a = (a % mod + mod) % mod;
	while(n) {
		if(n & 1) (ret *= a) %= mod;
		(a *= a) %= mod;
		n >>= 1;
	}
	return ret;
}

// others
struct fast_io {
	fast_io() {
		ios::sync_with_stdio(false);
		cin.tie(nullptr);
		cout << fixed << setprecision(15);
	}
} fast_io_;
const int inf = 1e9;
const ll INF = 1e18;
#pragma endregion

void main_();

int main() {
	main_();
	return 0;
}
#line 4 "h.cpp"

const int MAX_LEN_P = 11;
void main_() {
	INT(n, m);
	VEC(string, s, n);
	vector adj(n, vector(n, false));
	REP(i, m) {
		INT(x, y);
		adj[--x][--y] = true;
	}
	int N = n * n * MAX_LEN_P * 2;
	Graph g(N);
	auto left_id = [&](int l, int r, int len) {
		return l * n * MAX_LEN_P + r * MAX_LEN_P + len;
	};
	auto right_id = [&](int l, int r, int len) {
		return n * n * MAX_LEN_P + l * n * MAX_LEN_P + r * MAX_LEN_P + len;
	};

	map<pii, int> mp;

	//左が余っている場合
	REP(l, n) {
		REP(len, SZ(s[l]) + 1) {
			auto amari = s[l].substr(0, len);
			reverse(all(amari));
			REP(r, n) {
				REP(len2, SZ(s[r]) + 1) {
					auto kesu = s[r].substr(0, len2);
					if(amari != kesu) continue;
					REP(r2, n) {
						if(!adj[r2][r]) continue;
						int u = left_id(l, r2, len);
						int v = right_id(l, r, SZ(s[r]) - len2);
						g.add_edge(u, v, SZ(s[r]), 1);
						chmax(mp[{u, v}], SZ(s[r]));
					}
				}
			}
		}
	}

	//右が余る場合
	REP(r, n) {
		REP(len, SZ(s[r]) + 1) {
			auto amari = s[r].substr(SZ(s[r]) - len);
			reverse(all(amari));
			REP(l, n) {
				REP(len2, SZ(s[l]) + 1) {
					auto kesu = s[l].substr(SZ(s[l]) - len2);
					if(amari != kesu) continue;
					REP(l2, n) {
						if(!adj[l][l2]) continue;
						int u = right_id(l2, r, len);
						int v = left_id(l, r, SZ(s[l]) - len2);
						g.add_edge(u, v, SZ(s[l]), 1);
						chmax(mp[{u, v}], SZ(s[l]));
					}
				}
			}
		}
	}

	strongly_connected_components scc(g, 1);
	auto &h = scc.graph_compressed;

	N = SZ(h);

	vector dp(N, -inf);
	vector vis(N, false);

	REP(i, n) {
		//左を余らせる場合
		REP(len, SZ(s[i]) + 1) {
			string lef = s[i].substr(len);
			string rig = lef;
			reverse(all(rig));
			if(lef == rig) {
				dp[scc[left_id(i, i, len)]] = SZ(s[i]);
				vis[scc[left_id(i, i, len)]] = 1;
			}
		}

		//右を余らせる場合
		REP(len, SZ(s[i]) + 1) {
			string lef = s[i].substr(0, SZ(s[i]) - len);
			string rig = lef;
			reverse(all(rig));
			if(lef == rig) {
				dp[scc[right_id(i, i, len)]] = SZ(s[i]);
				vis[scc[right_id(i, i, len)]] = 1;
			}
		}
	}

	auto &groups = scc.groups;
	//debug(groups);

	REP(i, N) {
		if(SZ(groups[i]) != 1) dp[i] = inf;
	}

	debug(dp);

	auto tsort = h.topological_sort();

	foa(idx, tsort) {
		if(!vis[idx]) continue;
		for(auto [to, cost] : h[idx]) {
			chmax(dp[to], dp[idx] + mp[{groups[idx][0], groups[to][0]}]);
			vis[to] = 1;
		}
	}

	int res = 0;
	REP(i, n) {
		REP(j, n) {
			int lid = scc[left_id(i, j, 0)];
			if(vis[lid]) {
				chmax(res, dp[lid]);
			}
			int rid = scc[right_id(i, j, 0)];
			if(vis[rid]) {
				chmax(res, dp[rid]);
			}
		}
	}

	print(res >= inf ? -1 : res);
}
accuracy: 0.5816326530612245
time_ms: 100
memory_kb: 64072
final_rank: 16

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;


#define EMPTY 0 //空文字のインデックス
#define NUM 105
#define BALANCE 10

/*
 * diff = 右の余り-左の余り+10
 * バランスしているとき、diffは10
 *
 * */

struct Data{
	Data(int arg_next_id,int arg_add_len){
		next_node_id = arg_next_id;
		add_len = arg_add_len;
	}
	int next_node_id,add_len;
};

struct Node{

	int left_id,right_id,diff;
	vector<Data> NEXT;
};

struct Info{

	char word[11];
	int length;
};

struct State{
	State(int arg_node_id,int arg_sum_value){
		node_id = arg_node_id;
		sum_value = arg_sum_value;
	}
	int node_id,sum_value;
};

int V,E;
const int start = 0,goal = 1;
bool check[NUM][NUM];
Info info[NUM];
vector<Node> NODES;
map<pair<int,int>,int> MAP[21]; //map[diff][make_pair(left_id,right_id)] = node_id
map<pair<int,int>,int> dp[21];  //dp[diff][make_pair(left_id,right_id)] = 最大長

#define SIZE 200005

vector<int> G[SIZE];
vector<int> reverse_G[SIZE];
bool can_goal[SIZE];
bool cycle_FLG;
bool visited[SIZE];

void cycle_dfs(int start_id,int current_id){

	if(cycle_FLG)return;

	for(int i = 0; i < G[current_id].size(); i++){
		if(G[current_id][i] == start_id){

			cycle_FLG = true;
			return;
		}
		if(visited[G[current_id][i]])continue;
		visited[G[current_id][i]] = true;

		cycle_dfs(start_id,G[current_id][i]);
	}
}

void goal_dfs(int node_id){

	can_goal[node_id] = true;

	for(int i = 0; i < reverse_G[node_id].size(); i++){

		if(visited[reverse_G[node_id][i]])continue;
		visited[reverse_G[node_id][i]] = true;

		goal_dfs(reverse_G[node_id][i]);
	}
}

void start_dfs(int node_id){

	for(int i = 0; i < G[node_id].size(); i++){

		if(visited[G[node_id][i]])continue;
		visited[G[node_id][i]] = true;

		start_dfs(G[node_id][i]);
	}
}



int main(){

	scanf("%d %d",&V,&E);

	for(int i = 0; i <= V; i++){
		for(int k = 0; k <= V; k++){
			check[i][k] = false;
		}
	}

	int length;

	//空文字
	info[0].word[0] = '\0';
	info[0].length = 0;

	for(int i = 1; i <= V; i++){

		scanf("%s",info[i].word);

		for(length = 0; info[i].word[length] != '\0'; length++);

		info[i].length = length;
	}

	int from,to;

	for(int loop = 0; loop < E; loop++){

		scanf("%d %d",&from,&to);

		check[from][to] = true;
	}

	int num_nodes = 2;
	Node start_node,goal_node;

	start_node.diff = BALANCE;
	start_node.left_id = 0;
	start_node.right_id = 0;

	goal_node.diff = BALANCE;
	goal_node.left_id = 0;
	goal_node.right_id = 0;

	NODES.push_back(start_node);
	NODES.push_back(goal_node);


	//まず、スタートから遷移する最初のノードを作る

	/*★1つの単語★*/

	//全体が回文になっているものを探す。接続に制約があるので、回文になっている単語は全てノード化する

	for(int i = 1; i <= V; i++){

		bool FLG = true;
		for(int k = 0; k < info[i].length/2; k++){
			if(info[i].word[k] != info[i].word[info[i].length-1-k]){
				FLG = false;
				break;
			}
		}

		if(FLG){

			//ノード作成
			Node node;

			node.left_id = i;
			node.right_id = i;
			node.diff = BALANCE;

			NODES.push_back(node);

			MAP[BALANCE][make_pair(i,i)] = num_nodes; //マップにIDを登録

			//スタートからエッジを張る
			NODES[start].NEXT.push_back(Data(num_nodes,info[i].length));
			G[start].push_back(num_nodes);
			reverse_G[num_nodes].push_back(start);

			//バランスしたので、ゴールにエッジを張る
			NODES[num_nodes].NEXT.push_back(Data(goal,0));
			G[num_nodes].push_back(goal);
			reverse_G[goal].push_back(num_nodes);

			num_nodes++;
		}
	}


	//左端固定
	for(int i = 1; i <= V; i++){

		for(int right = 0; right <= info[i].length-2; right++){
			bool FLG = true;

			//0～rightが回文になっているか
			int tmp_len = right+1;

			for(int k = 0; k < tmp_len/2; k++){
				if(info[i].word[k] != info[i].word[tmp_len-1-k]){
					FLG = false;
					break;
				}
			}

			if(FLG){

				//ノード作成
				Node node;

				node.left_id = i;
				node.right_id = i;
				node.diff = (info[i].length-(right+1))+BALANCE;

				NODES.push_back(node);

				MAP[node.diff][make_pair(node.left_id,node.right_id)] = num_nodes; //マップにIDを登録

				//スタートからエッジを張る
				NODES[start].NEXT.push_back(Data(num_nodes,info[i].length));
				G[start].push_back(num_nodes);
				reverse_G[num_nodes].push_back(start);

				num_nodes++;
			}
		}
	}

	//右端固定
	for(int i = 1; i <= V; i++){

		for(int left = 1; left <= info[i].length-1; left++){
			bool FLG = true;

			//left～info[i].length-1が回文になっているか
			int tmp_len = info[i].length-left;

			for(int k = 0; k < tmp_len/2; k++){
				if(info[i].word[left+k] != info[i].word[info[i].length-1-k]){
					FLG = false;
					break;
				}
			}

			if(FLG){

				//ノード作成
				Node node;

				node.left_id = i;
				node.right_id = i;
				node.diff = -left+BALANCE;

				NODES.push_back(node);

				MAP[node.diff][make_pair(node.left_id,node.right_id)] = num_nodes; //マップにIDを登録

				//スタートからエッジを張る
				NODES[start].NEXT.push_back(Data(num_nodes,info[i].length));
				G[start].push_back(num_nodes);
				reverse_G[num_nodes].push_back(start);

				num_nodes++;
			}
		}
	}

	/*★2つの単語★*/
	for(int L = 1; L <= V; L++){
		for(int R = 1; R <= V; R++){
			if(!check[L][R])continue;

			bool FLG = true;
			int tmp_len = min(info[L].length,info[R].length);

			for(int k = 0; k < tmp_len; k++){
				if(info[L].word[info[L].length-1-k] != info[R].word[k]){
					FLG = false;
					break;
				}
			}

			if(!FLG)continue;

			//ノード作成
			Node node;

			node.left_id = L;
			node.right_id = R;
			node.diff = (info[R].length-info[L].length)+BALANCE;

			auto at = MAP[node.diff].find(make_pair(node.left_id,node.right_id));

			if(at != MAP[node.diff].end()){ //★注意★

				continue;
			}

			NODES.push_back(node);

			MAP[node.diff][make_pair(L,R)] = num_nodes; //マップにIDを登録

			//スタートからエッジを張る
			NODES[start].NEXT.push_back(Data(num_nodes,info[L].length+info[R].length));
			G[start].push_back(num_nodes);
			reverse_G[num_nodes].push_back(start);


			if(node.diff == BALANCE){

				//バランスしたので、ゴールにエッジを張る
				NODES[num_nodes].NEXT.push_back(Data(goal,0));
				G[num_nodes].push_back(goal);
				reverse_G[goal].push_back(num_nodes);
			}

			num_nodes++;
		}
	}

	//発生し得る状態を全列挙
	queue<int> Q;

	for(int i = 0; i < NODES[start].NEXT.size(); i++){

		Q.push(NODES[start].NEXT[i].next_node_id);
	}

	while(!Q.empty()){

		int node_id = Q.front();
		Q.pop();

		if(NODES[node_id].diff <= BALANCE){ //右に文字追加

			for(int i = 1; i <= V; i++){

				if(!check[NODES[node_id].right_id][i])continue;

				//少なくとも重なる部分が全て一致するか調べる
				bool FLG = true;
				int left_diff = abs(NODES[node_id].diff-BALANCE);
				int tmp_len = min(left_diff,info[i].length);
				int left_id = NODES[node_id].left_id;

				for(int k = 0; k < tmp_len; k++){

					if(info[left_id].word[left_diff-1-k] != info[i].word[k]){
						FLG = false;
						break;
					}
				}

				if(!FLG)continue;

				Node node;
				node.diff = info[i].length-left_diff+BALANCE;
				node.left_id = NODES[node_id].left_id;
				node.right_id = i;

				auto at = MAP[node.diff].find(make_pair(node.left_id,node.right_id));

				if(at != MAP[node.diff].end()){ //出現済:エッジだけ張る

					int next_node = MAP[node.diff][make_pair(node.left_id,node.right_id)];

					NODES[node_id].NEXT.push_back(Data(next_node,info[i].length));
					G[node_id].push_back(next_node);
					reverse_G[next_node].push_back(node_id);

					continue;
				}

				NODES.push_back(node); //初登場

				MAP[node.diff][make_pair(node.left_id,node.right_id)] = num_nodes;

				//node_idからエッジを張る
				NODES[node_id].NEXT.push_back(Data(num_nodes,info[i].length));
				G[node_id].push_back(num_nodes);
				reverse_G[num_nodes].push_back(node_id);


				if(node.diff == BALANCE){ //バランスしていれば、ゴールにエッジを張る

					NODES[num_nodes].NEXT.push_back(Data(goal,0));
					G[num_nodes].push_back(goal);
					reverse_G[goal].push_back(num_nodes);

				}

				Q.push(num_nodes);

				num_nodes++;
			}

		}

		if(NODES[node_id].diff >= BALANCE){ //左に文字追加

			for(int i = 1; i <= V; i++){

				if(!check[i][NODES[node_id].left_id])continue;

				//少なくとも重なる部分が全て一致するか調べる
				bool FLG = true;
				int right_diff = NODES[node_id].diff-BALANCE;
				int tmp_len = min(right_diff,info[i].length);
				int right_id = NODES[node_id].right_id;

				for(int k = 0; k < tmp_len; k++){

					if(info[right_id].word[info[right_id].length-right_diff+k] != info[i].word[info[i].length-1-k]){
						FLG = false;
						break;
					}
				}

				if(!FLG)continue;

				Node node;
				node.diff = right_diff-info[i].length+BALANCE;
				node.left_id = i;
				node.right_id = NODES[node_id].right_id;

				auto at = MAP[node.diff].find(make_pair(node.left_id,node.right_id));

				if(at != MAP[node.diff].end()){  //出現済:エッジだけ張る

					int next_node = MAP[node.diff][make_pair(node.left_id,node.right_id)];

					NODES[node_id].NEXT.push_back(Data(next_node,info[i].length));
					G[node_id].push_back(next_node);
					reverse_G[next_node].push_back(node_id);

					continue;
				}

				NODES.push_back(node); //初出現なのでノード追加
				MAP[node.diff][make_pair(node.left_id,node.right_id)] = num_nodes;

				//node_idからエッジを張る
				NODES[node_id].NEXT.push_back(Data(num_nodes,info[i].length));
				G[node_id].push_back(num_nodes);
				reverse_G[num_nodes].push_back(node_id);

				if(node.diff == BALANCE){ //バランスしていれば、ゴールにエッジを張る

					NODES[num_nodes].NEXT.push_back(Data(goal,0));
					G[num_nodes].push_back(goal);
					reverse_G[goal].push_back(num_nodes);
				}

				Q.push(num_nodes);

				num_nodes++;
			}
		}
	}

	//ゴールできるか調べる
	for(int i = 0; i < num_nodes; i++){

		visited[i] = false;
	}
	visited[start] = true;
	start_dfs(start);

	if(!visited[goal]){

		printf("0\n");
		return 0;
	}

	//ゴールできるノードを探す
	for(int i = 0; i < num_nodes; i++){

		can_goal[i] = false;
		visited[i] = false;
	}
	visited[goal] = true;
	goal_dfs(goal);


	//ゴールできるものの中で、閉路に属するノードがないか調べる
	for(int i = 0; i < num_nodes; i++){
		if(can_goal[i] == false)continue;

		for(int k = 0; k < num_nodes; k++){

			visited[k] = false;
		}
		cycle_FLG = false;

		visited[i] = true;
		cycle_dfs(i,i);

		if(cycle_FLG){

			printf("-1\n");
			return 0;
		}
	}

	//dp計算する
	int ans = 0;

	//DAGになってるはずなのでdp計算
	queue<State> final_Q;

	dp[NODES[start].diff][make_pair(NODES[start].left_id,NODES[start].right_id)] = 0;
	final_Q.push(State(start,0));

	while(!final_Q.empty()){

		State state = final_Q.front();
		final_Q.pop();

		auto at = dp[NODES[state.node_id].diff].find(make_pair(NODES[state.node_id].left_id,NODES[state.node_id].right_id));

		if(at != dp[NODES[state.node_id].diff].end() && state.sum_value < dp[NODES[state.node_id].diff][make_pair(NODES[state.node_id].left_id,NODES[state.node_id].right_id)]){

			continue;
		}

		if(state.node_id == goal){

			ans = max(ans,state.sum_value);
		}

		for(int i = 0; i < NODES[state.node_id].NEXT.size(); i++){

			int next_node = NODES[state.node_id].NEXT[i].next_node_id;
			if(!can_goal[next_node])continue;

			int next_value = state.sum_value+NODES[state.node_id].NEXT[i].add_len;

			at = dp[NODES[next_node].diff].find(make_pair(NODES[next_node].left_id,NODES[next_node].right_id));

			if(at == dp[NODES[next_node].diff].end()){

				dp[NODES[next_node].diff][make_pair(NODES[next_node].left_id,NODES[next_node].right_id)] = next_value;

				final_Q.push(State(next_node,next_value));

			}else{

				if(dp[NODES[next_node].diff][make_pair(NODES[next_node].left_id,NODES[next_node].right_id)] < next_value){

					dp[NODES[next_node].diff][make_pair(NODES[next_node].left_id,NODES[next_node].right_id)] = next_value;
					final_Q.push(State(next_node,next_value));
				}
			}
		}
	}

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

	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 17068
final_rank: 2

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;


#define EMPTY 0 //空文字のインデックス
#define NUM 105
#define BALANCE 10

/*
 * diff = 右の余り-左の余り+10
 * バランスしているとき、diffは10
 *
 * */

struct Data{
	Data(int arg_next_id,int arg_add_len){
		next_node_id = arg_next_id;
		add_len = arg_add_len;
	}
	int next_node_id,add_len;
};

struct Node{

	int left_id,right_id,diff;
	vector<Data> NEXT;
};

struct Info{

	char word[11];
	int length;
};

struct State{
	State(int arg_node_id,int arg_sum_value){
		node_id = arg_node_id;
		sum_value = arg_sum_value;
	}
	int node_id,sum_value;
};

int V,E;
const int start = 0,goal = 1;
bool check[NUM][NUM];
Info info[NUM];
vector<Node> NODES;
map<pair<int,int>,int> MAP[21]; //map[diff][make_pair(left_id,right_id)] = node_id
map<pair<int,int>,int> dp[21];  //dp[diff][make_pair(left_id,right_id)] = 最大長

#define SIZE 200005

vector<int> G[SIZE];
vector<int> reverse_G[SIZE];
bool can_goal[SIZE];
bool cycle_FLG;
bool visited[SIZE];

void cycle_dfs(int start_id,int current_id){

	if(cycle_FLG)return;

	for(int i = 0; i < G[current_id].size(); i++){
		if(G[current_id][i] == start_id){

			cycle_FLG = true;
			return;
		}
		if(visited[G[current_id][i]])continue;
		/*if(visited[G[current_id][i]]){
			if(can_goal[G[current_id][i]]){
			cycle_FLG = true;
			return;
			}
			continue;
		}*/
		visited[G[current_id][i]] = true;

		cycle_dfs(start_id,G[current_id][i]);
	}
}

void goal_dfs(int node_id){

	can_goal[node_id] = true;

	for(int i = 0; i < reverse_G[node_id].size(); i++){

		if(visited[reverse_G[node_id][i]])continue;
		visited[reverse_G[node_id][i]] = true;

		goal_dfs(reverse_G[node_id][i]);
	}
}

void start_dfs(int node_id){

	for(int i = 0; i < G[node_id].size(); i++){

		if(visited[G[node_id][i]])continue;
		visited[G[node_id][i]] = true;

		start_dfs(G[node_id][i]);
	}
}



int main(){

	scanf("%d %d",&V,&E);

	for(int i = 0; i <= V; i++){
		for(int k = 0; k <= V; k++){
			check[i][k] = false;
		}
	}

	int length;

	//空文字
	info[0].word[0] = '@';
	info[0].word[1] = '\0';
	info[0].length = 0;

	for(int i = 1; i <= V; i++){

		scanf("%s",info[i].word);

		for(length = 0; info[i].word[length] != '\0'; length++);

		info[i].length = length;
	}

	int from,to;

	for(int loop = 0; loop < E; loop++){

		scanf("%d %d",&from,&to);

		check[from][to] = true;
	}

	int num_nodes = 2;
	Node start_node,goal_node;

	start_node.diff = BALANCE;
	start_node.left_id = 0;
	start_node.right_id = 0;

	goal_node.diff = BALANCE;
	goal_node.left_id = 0;
	goal_node.right_id = 0;

	NODES.push_back(start_node);
	NODES.push_back(goal_node);


	//まず、スタートから遷移する最初のノードを作る

	/*★1つの単語★*/

	//全体が回文になっているものを探す。接続に制約があるので、回文になっている単語は全てノード化する

	for(int i = 1; i <= V; i++){

		bool FLG = true;
		for(int k = 0; k < info[i].length/2; k++){
			if(info[i].word[k] != info[i].word[info[i].length-1-k]){
				FLG = false;
				break;
			}
		}

		if(FLG){

			//ノード作成
			Node node;

			node.left_id = i;
			node.right_id = i;
			node.diff = BALANCE;

			NODES.push_back(node);

			MAP[BALANCE][make_pair(i,i)] = num_nodes; //マップにIDを登録

			//スタートからエッジを張る
			NODES[start].NEXT.push_back(Data(num_nodes,info[i].length));
			G[start].push_back(num_nodes);
			reverse_G[num_nodes].push_back(start);

			//バランスしたので、ゴールにエッジを張る
			NODES[num_nodes].NEXT.push_back(Data(goal,0));
			G[num_nodes].push_back(goal);
			reverse_G[goal].push_back(num_nodes);

			num_nodes++;
		}
	}


	//左端固定
	for(int i = 1; i <= V; i++){

		for(int right = 0; right <= info[i].length-2; right++){
			bool FLG = true;

			//0～rightが回文になっているか
			int tmp_len = right+1;

			for(int k = 0; k < tmp_len/2; k++){
				if(info[i].word[k] != info[i].word[tmp_len-1-k]){
					FLG = false;
					break;
				}
			}

			if(FLG){

				//ノード作成
				Node node;

				node.left_id = i;
				node.right_id = i;
				node.diff = (info[i].length-(right+1))+BALANCE;

				NODES.push_back(node);

				MAP[node.diff][make_pair(node.left_id,node.right_id)] = num_nodes; //マップにIDを登録

				//スタートからエッジを張る
				NODES[start].NEXT.push_back(Data(num_nodes,info[i].length));
				G[start].push_back(num_nodes);
				reverse_G[num_nodes].push_back(start);

				num_nodes++;
			}
		}
	}

	//右端固定
	for(int i = 1; i <= V; i++){

		for(int left = 1; left <= info[i].length-1; left++){
			bool FLG = true;

			//left～info[i].length-1が回文になっているか
			int tmp_len = info[i].length-left;

			for(int k = 0; k < tmp_len/2; k++){
				if(info[i].word[left+k] != info[i].word[info[i].length-1-k]){
					FLG = false;
					break;
				}
			}

			if(FLG){

				//ノード作成
				Node node;

				node.left_id = i;
				node.right_id = i;
				node.diff = -left+BALANCE;

				NODES.push_back(node);

				MAP[node.diff][make_pair(node.left_id,node.right_id)] = num_nodes; //マップにIDを登録

				//スタートからエッジを張る
				NODES[start].NEXT.push_back(Data(num_nodes,info[i].length));
				G[start].push_back(num_nodes);
				reverse_G[num_nodes].push_back(start);

				num_nodes++;
			}
		}
	}

	/*★2つの単語★*/
	for(int L = 1; L <= V; L++){
		for(int R = 1; R <= V; R++){
			if(!check[L][R])continue;

			bool FLG = true;
			int tmp_len = min(info[L].length,info[R].length);

			for(int k = 0; k < tmp_len; k++){
				if(info[L].word[info[L].length-1-k] != info[R].word[k]){
					FLG = false;
					break;
				}
			}

			if(!FLG)continue;

			//ノード作成
			Node node;

			node.left_id = L;
			node.right_id = R;
			node.diff = (info[R].length-info[L].length)+BALANCE;

			auto at = MAP[node.diff].find(make_pair(node.left_id,node.right_id));

			if(at != MAP[node.diff].end()){ //★注意★

				continue;
			}

			NODES.push_back(node);

			MAP[node.diff][make_pair(L,R)] = num_nodes; //マップにIDを登録

			//スタートからエッジを張る
			NODES[start].NEXT.push_back(Data(num_nodes,info[L].length+info[R].length));
			G[start].push_back(num_nodes);
			reverse_G[num_nodes].push_back(start);


			if(node.diff == BALANCE){

				//バランスしたので、ゴールにエッジを張る
				NODES[num_nodes].NEXT.push_back(Data(goal,0));
				G[num_nodes].push_back(goal);
				reverse_G[goal].push_back(num_nodes);
			}

			num_nodes++;
		}
	}

	//発生し得る状態を全列挙
	queue<int> Q;

	for(int i = 0; i < NODES[start].NEXT.size(); i++){

		Q.push(NODES[start].NEXT[i].next_node_id);
	}



	/*printf("Minhi!\n");
	return 0;
*/
	while(!Q.empty()){

		int node_id = Q.front();
		Q.pop();

		if(NODES[node_id].diff <= BALANCE){ //右に文字追加

			for(int i = 1; i <= V; i++){

				if(!check[NODES[node_id].right_id][i])continue;

				//少なくとも重なる部分が全て一致するか調べる
				bool FLG = true;
				int left_diff = abs(NODES[node_id].diff-BALANCE);
				int tmp_len = min(left_diff,info[i].length);
				int left_id = NODES[node_id].left_id;

				for(int k = 0; k < tmp_len; k++){

					if(info[left_id].word[left_diff-1-k] != info[i].word[k]){
						FLG = false;
						break;
					}
				}

				if(!FLG)continue;

				Node node;
				node.diff = info[i].length-left_diff+BALANCE;
				node.left_id = NODES[node_id].left_id;
				node.right_id = i;

				auto at = MAP[node.diff].find(make_pair(node.left_id,node.right_id));

				if(at != MAP[node.diff].end()){ //出現済:エッジだけ張る

					int next_node = MAP[node.diff][make_pair(node.left_id,node.right_id)];

					NODES[node_id].NEXT.push_back(Data(next_node,info[i].length));
					G[node_id].push_back(next_node);
					reverse_G[next_node].push_back(node_id);

					continue;
				}

				NODES.push_back(node); //初登場

				MAP[node.diff][make_pair(node.left_id,node.right_id)] = num_nodes;

				//node_idからエッジを張る
				NODES[node_id].NEXT.push_back(Data(num_nodes,info[i].length));
				G[node_id].push_back(num_nodes);
				reverse_G[num_nodes].push_back(node_id);


				if(node.diff == BALANCE){ //バランスしていれば、ゴールにエッジを張る

					NODES[num_nodes].NEXT.push_back(Data(goal,0));
					G[num_nodes].push_back(goal);
					reverse_G[goal].push_back(num_nodes);

				}

				Q.push(num_nodes);

				num_nodes++;
			}

		}

		if(NODES[node_id].diff >= BALANCE){ //左に文字追加

			for(int i = 1; i <= V; i++){

				if(!check[i][NODES[node_id].left_id])continue;

				//少なくとも重なる部分が全て一致するか調べる
				bool FLG = true;
				int right_diff = NODES[node_id].diff-BALANCE;
				int tmp_len = min(right_diff,info[i].length);
				int right_id = NODES[node_id].right_id;

				for(int k = 0; k < tmp_len; k++){

					if(info[right_id].word[info[right_id].length-right_diff+k] != info[i].word[info[i].length-1-k]){
						FLG = false;
						break;
					}
				}

				if(!FLG)continue;

				Node node;
				node.diff = right_diff-info[i].length+BALANCE;
				node.left_id = i;
				node.right_id = NODES[node_id].right_id;

				auto at = MAP[node.diff].find(make_pair(node.left_id,node.right_id));

				if(at != MAP[node.diff].end()){  //出現済:エッジだけ張る

					int next_node = MAP[node.diff][make_pair(node.left_id,node.right_id)];

					NODES[node_id].NEXT.push_back(Data(next_node,info[i].length));
					G[node_id].push_back(next_node);
					reverse_G[next_node].push_back(node_id);

					continue;
				}

				NODES.push_back(node); //初出現なのでノード追加
				MAP[node.diff][make_pair(node.left_id,node.right_id)] = num_nodes;

				//node_idからエッジを張る
				NODES[node_id].NEXT.push_back(Data(num_nodes,info[i].length));
				G[node_id].push_back(num_nodes);
				reverse_G[num_nodes].push_back(node_id);

				if(node.diff == BALANCE){ //バランスしていれば、ゴールにエッジを張る

					NODES[num_nodes].NEXT.push_back(Data(goal,0));
					G[num_nodes].push_back(goal);
					reverse_G[goal].push_back(num_nodes);
				}

				Q.push(num_nodes);

				num_nodes++;
			}
		}
	}

	//ゴールできるか調べる
	for(int i = 0; i < num_nodes; i++){

		visited[i] = false;
	}
	visited[start] = true;
	start_dfs(start);

	if(!visited[goal]){

		printf("0\n");
		return 0;
	}

	//ゴールできるノードを探す
	for(int i = 0; i < num_nodes; i++){

		can_goal[i] = false;
		visited[i] = false;
	}
	visited[goal] = true;
	goal_dfs(goal);


	//ゴールできるものの中で、閉路に属するノードがないか調べる
	for(int i = 0; i < num_nodes; i++){
		if(can_goal[i] == false)continue;

		for(int k = 0; k < num_nodes; k++){

			visited[k] = false;
		}
		cycle_FLG = false;

		visited[i] = true;
		cycle_dfs(i,i);

		if(cycle_FLG){

			printf("-1\n");
			return 0;
		}
	}

	//dp計算する
	int ans = 0;

	//DAGになってるはずなのでdp計算
	queue<State> final_Q;

	dp[NODES[start].diff][make_pair(NODES[start].left_id,NODES[start].right_id)] = 0;
	final_Q.push(State(start,0));

	while(!final_Q.empty()){

		State state = final_Q.front();
		final_Q.pop();

		auto at = dp[NODES[state.node_id].diff].find(make_pair(NODES[state.node_id].left_id,NODES[state.node_id].right_id));

		if(at != dp[NODES[state.node_id].diff].end() && state.sum_value < dp[NODES[state.node_id].diff][make_pair(NODES[state.node_id].left_id,NODES[state.node_id].right_id)]){

			continue;
		}

		if(state.node_id == goal){

			ans = max(ans,state.sum_value);
		}

		for(int i = 0; i < NODES[state.node_id].NEXT.size(); i++){

			int next_node = NODES[state.node_id].NEXT[i].next_node_id;
			if(!can_goal[next_node])continue;

			int next_value = state.sum_value+NODES[state.node_id].NEXT[i].add_len;

			at = dp[NODES[next_node].diff].find(make_pair(NODES[next_node].left_id,NODES[next_node].right_id));

			if(at == dp[NODES[next_node].diff].end()){

				dp[NODES[next_node].diff][make_pair(NODES[next_node].left_id,NODES[next_node].right_id)] = next_value;

				final_Q.push(State(next_node,next_value));

			}else{

				if(dp[NODES[next_node].diff][make_pair(NODES[next_node].left_id,NODES[next_node].right_id)] < next_value){

					dp[NODES[next_node].diff][make_pair(NODES[next_node].left_id,NODES[next_node].right_id)] = next_value;
					final_Q.push(State(next_node,next_value));
				}
			}
		}
	}

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

	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 16968
final_rank: 1

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

//INSERT ABOVE HERE
signed main(){
  const int D = 11;
 
  int n,m;
  cin>>n>>m;
  vector<string> s(n);
  for(int i=0;i<n;i++) cin>>s[i];

  vector<vector<int> > E(n,vector<int>(n,0));
  for(int i=0;i<m;i++){
    int x,y;
    cin>>x>>y;
    x--;y--;
    E[x][y]=1;
  }

  vector<int> l(n);
  for(int i=0;i<n;i++) l[i]=s[i].size();


  using T = tuple<int, int, int>;
  auto idx=[&](int d,int x,int y){return (D+d)*n*n+x*n+y;};
  auto inv=[&](int v){return T(v/(n*n)-D,v%(n*n)/n,v%n);};
  int sz=idx(D,n-1,n-1)+1;

  queue<T> qt;
  vector<int> dp(sz,-1);
  
  // center inside s[x]
  for(int x=0;x<n;x++){
    // even
    for(int i=1;i<l[x];i++){
      // [0, i) + [i, l)
      int k=min(i,l[x]-i),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+j];
      if(ok){
	int d=i-(l[x]-i);
	qt.emplace(d,x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
    // odd
    for(int i=0;i<l[x];i++){
      // [0, i) + [i, i+1) + [i+1, l)
      int k=min(i,l[x]-(i+1)),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+1+j];
      if(ok){
	int d=i-(l[x]-(i+1));
	qt.emplace(i-(l[x]-(i+1)),x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
  }

  // s[x] |center| s[y] (obviously even)
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      if(!E[x][y]) continue;
      int k=min(l[x],l[y]),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][l[x]-(j+1)]==s[y][j];
      if(ok){
	int d=l[x]-l[y];
	qt.emplace(d,x,y);
	chmax(dp[idx(d,x,y)],l[x]+l[y]);
      }
    }    
  }

  using P = pair<int, int>;
  vector<vector<P> > G(sz),R(sz);
  
  for(int d=-D;d<=D;d++){
    for(int x=0;x<n;x++){
      for(int y=0;y<n;y++){
	int v=idx(d,x,y);
	for(int i=0;i<n;i++){
	  // s[i] s[x] s[y]
	  if(d<=0&&E[i][x]){
	    int k=min(-d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][l[i]-(j+1)]==s[y][l[y]+d+j];
	    if(ok){	     
	      int nd=d+l[i];
	      int u=idx(nd,i,y);
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	  // s[x] s[y] s[i]
	  if(d>=0&&E[y][i]){
	    int k=min(d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][j]==s[x][d-(j+1)];
	    if(ok){
	      int nd=d-l[i];
	      int u=idx(nd,x,i);
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	}
      }           
    }
  }

  // find nodes that can reached from starts
  while(!qt.empty()){
    int d,x,y;
    tie(d,x,y)=qt.front();qt.pop();
    int v=idx(d,x,y);
    for(auto e:G[v]){
      int u,c;
      tie(u,c)=e;
      if(~dp[u]) continue;
      chmax(dp[u],dp[v]+c);
      qt.emplace(inv(u));      
    }    
  }

  vector<int> dp2(sz,-1),used(sz,0),visit(sz,0);
  queue<int> qu;
  
  function<int(int)> dfs=
    [&](int v){
      int &res=dp2[v];
      if(used[v]) return res;
      
      visit[v]=1;
      qu.emplace(v);
      res=dp[v];
      
      for(auto e:R[v]){
	int u,c;
	tie(u,c)=e;
	if(dp[u]<0) continue;
	if(visit[u]==1){
	  cout<<-1<<endl;
	  exit(0);
	}
	chmax(res,dfs(u)+c);
      }
      
      visit[v]=0;
      return res;
    };
  
  int ans=0;
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      int v=idx(0,x,y);
      if(~dp[v]) chmax(ans,dfs(v));
      while(!qu.empty()){
	used[qu.front()]=1;
	visit[qu.front()]=2;
	qu.pop();
      }
    }
  }

  cout<<ans<<endl;
  return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 28380
final_rank: 4

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

//INSERT ABOVE HERE
signed main(){
  const int D = 11;
 
  int n,m;
  cin>>n>>m;
  vector<string> s(n);
  for(int i=0;i<n;i++) cin>>s[i];

  vector<vector<int> > E(n,vector<int>(n,0));
  for(int i=0;i<m;i++){
    int x,y;
    cin>>x>>y;
    x--;y--;
    E[x][y]=1;
  }

  vector<int> l(n);
  for(int i=0;i<n;i++) l[i]=s[i].size();


  using T = tuple<int, int, int>;
  auto idx=[&](int d,int x,int y){return (D+d)*n*n+x*n+y;};
  auto inv=[&](int v){return T(v/(n*n)-D,v%(n*n)/n,v%n);};
  int sz=idx(D,n-1,n-1)+1;

  queue<T> qt;
  vector<int> dp(sz,-1);
  
  // center inside s[x]
  for(int x=0;x<n;x++){
    // even
    for(int i=1;i<l[x];i++){
      // [0, i) + [i, l)
      int k=min(i,l[x]-i),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+j];
      if(ok){
	int d=i-(l[x]-i);
	qt.emplace(d,x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
    // odd
    for(int i=0;i<l[x];i++){
      // [0, i) + [i, i+1) + [i+1, l)
      int k=min(i,l[x]-(i+1)),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+1+j];
      if(ok){
	int d=i-(l[x]-(i+1));
	qt.emplace(i-(l[x]-(i+1)),x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
  }

  // s[x] |center| s[y] (obviously even)
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      if(!E[x][y]) continue;
      int k=min(l[x],l[y]),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][l[x]-(j+1)]==s[y][j];
      if(ok){
	int d=l[x]-l[y];
	qt.emplace(d,x,y);
	chmax(dp[idx(d,x,y)],l[x]+l[y]);
      }
    }    
  }

  using P = pair<int, int>;
  vector<vector<P> > G(sz),R(sz);
  
  for(int d=-D;d<=D;d++){
    for(int x=0;x<n;x++){
      for(int y=0;y<n;y++){
	int v=idx(d,x,y);
	//cout<<v<<":"<<d<<" "<<x<<" "<<y<<endl;
	for(int i=0;i<n;i++){
	  // s[i] s[x] s[y]
	  if(d<=0&&E[i][x]){
	    int k=min(-d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][l[i]-(j+1)]==s[y][l[y]+d+j];
	    if(ok){	     
	      int nd=d+l[i];
	      int u=idx(nd,i,y);
	      //cout<<v<<" -> "<<u<<endl;
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	  // s[x] s[y] s[i]
	  if(d>=0&&E[y][i]){
	    int k=min(d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][j]==s[x][d-(j+1)];
	    if(ok){
	      int nd=d-l[i];
	      int u=idx(nd,x,i);
	      //cout<<v<<" -> "<<u<<endl;
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	}
      }           
    }
  }

  // find nodes that can reached from starts
  while(!qt.empty()){
    int d,x,y;
    tie(d,x,y)=qt.front();qt.pop();
    int v=idx(d,x,y);
    //cout<<dp[v]<<":"<<d<<" "<<x<<" "<<y<<endl;
    for(auto e:G[v]){
      int u,c;
      tie(u,c)=e;
      if(~dp[u]) continue;
      chmax(dp[u],dp[v]+c);
      qt.emplace(inv(u));      
    }    
  }

  vector<int> dp2(sz,-1),used(sz,0),visit(sz,0);
  queue<int> qu;
  
  function<int(int)> dfs=
    [&](int v){
      int &res=dp2[v];
      if(used[v]) return res;
      //cout<<v<<"****"<<s[get<1>(inv(v))]<<" "<<s[get<2>(inv(v))]<<endl;
      //cout<<get<0>(inv(v))<<":"<<get<1>(inv(v))<<":"<<get<2>(inv(v))<<endl;
      
      visit[v]=1;
      qu.emplace(v);
      res=dp[v];
      
      for(auto e:R[v]){
	int u,c;
	tie(u,c)=e;
	if(dp[u]<0) continue;
	//cout<<setw(6)<<v<<":"<<setw(3)<<dp[v]<<":"<<setw(3)<<visit[v]<<endl;
	//cout<<setw(6)<<u<<"+"<<setw(3)<<dp[u]<<"+"<<setw(3)<<visit[u]<<endl;
	if(visit[u]==1){
	  cout<<-1<<endl;
	  exit(0);
	}
	chmax(res,dfs(u)+c);
      }
      visit[v]=0;

      //cout<<v<<"::"<<res<<endl;
      return res;
    };
  
  int ans=0;
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      int v=idx(0,x,y);
      if(~dp[v]) chmax(ans,dfs(v));
      while(!qu.empty()){
	used[qu.front()]=1;
	visit[qu.front()]=2;
	qu.pop();
      }
    }
  }

  cout<<ans<<endl;
  return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 28520
final_rank: 5

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

//INSERT ABOVE HERE
signed main(){
  const int D = 11;
 
  int n,m;
  cin>>n>>m;
  vector<string> s(n);
  for(int i=0;i<n;i++) cin>>s[i];

  vector<vector<int> > E(n,vector<int>(n,0));
  for(int i=0;i<m;i++){
    int x,y;
    cin>>x>>y;
    x--;y--;
    E[x][y]=1;
  }

  vector<int> l(n);
  for(int i=0;i<n;i++) l[i]=s[i].size();


  using T = tuple<int, int, int>;
  auto idx=[&](int d,int x,int y){return (D+d)*n*n+x*n+y;};
  auto inv=[&](int v){return T(v/(n*n)-D,v%(n*n)/n,v%n);};
  int sz=idx(D,n-1,n-1)+1;

  queue<T> qt;
  vector<int> dp(sz,-1);
  
  // center inside s[x]
  for(int x=0;x<n;x++){
    // even
    for(int i=1;i<l[x];i++){
      // [0, i) + [i, l)
      int k=min(i,l[x]-i),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+j];
      if(ok){
	int d=i-(l[x]-i);
	qt.emplace(d,x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
    // odd
    for(int i=0;i<l[x];i++){
      // [0, i) + [i, i+1) + [i+1, l)
      int k=min(i,l[x]-(i+1)),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+1+j];
      if(ok){
	int d=i-(l[x]-(i+1));
	qt.emplace(i-(l[x]-(i+1)),x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
  }

  // s[x] |center| s[y] (obviously even)
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      if(!E[x][y]) continue;
      int k=min(l[x],l[y]),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][l[x]-(j+1)]==s[y][j];
      if(ok){
	int d=l[x]-l[y];
	qt.emplace(d,x,y);
	chmax(dp[idx(d,x,x)],l[x]+l[y]);
      }
    }    
  }

  using P = pair<int, int>;
  vector<vector<P> > G(sz),R(sz);
  
  for(int d=-D;d<=D;d++){
    for(int x=0;x<n;x++){
      for(int y=0;y<n;y++){
	int v=idx(d,x,y);
	//cout<<v<<":"<<d<<" "<<x<<" "<<y<<endl;
	for(int i=0;i<n;i++){
	  // s[i] s[x] s[y]
	  if(d<=0&&E[i][x]){
	    int k=min(-d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][l[i]-(j+1)]==s[y][l[y]+d+j];
	    if(ok){	     
	      int nd=d+l[i];
	      int u=idx(nd,i,y);
	      //cout<<v<<" -> "<<u<<endl;
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	  // s[x] s[y] s[i]
	  if(d>=0&&E[y][i]){
	    int k=min(d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][j]==s[x][d-(j+1)];
	    if(ok){
	      int nd=d-l[i];
	      int u=idx(nd,x,i);
	      //cout<<v<<" -> "<<u<<endl;
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	}
      }           
    }
  }

  // find nodes that can reached from starts
  while(!qt.empty()){
    int d,x,y;
    tie(d,x,y)=qt.front();qt.pop();
    int v=idx(d,x,y);
    //cout<<dp[v]<<":"<<d<<" "<<x<<" "<<y<<endl;
    for(auto e:G[v]){
      int u,c;
      tie(u,c)=e;
      if(~dp[u]) continue;
      chmax(dp[u],dp[v]+c);
      qt.emplace(inv(u));      
    }    
  }

  vector<int> dp2(sz,-1),used(sz,0),visit(sz,0);
  queue<int> qu;
  
  function<int(int)> dfs=
    [&](int v){
      int &res=dp2[v];
      if(used[v]) return res;
      //cout<<v<<"****"<<endl;
      //cout<<get<0>(inv(v))<<":"<<get<1>(inv(v))<<":"<<get<2>(inv(v))<<endl;
      
      visit[v]=1;
      qu.emplace(v);
      res=dp[v];
      for(auto e:R[v]){
	int u,c;
	tie(u,c)=e;
	if(dp[u]<0) continue;
	//cout<<setw(6)<<v<<":"<<setw(3)<<dp[v]<<":"<<setw(3)<<visit[v]<<endl;
	//cout<<setw(6)<<u<<"+"<<setw(3)<<dp[u]<<"+"<<setw(3)<<visit[u]<<endl;
	if(visit[u]==1){
	  cout<<-1<<endl;
	  exit(0);
	}
	chmax(res,dfs(u)+c);
      }
      visit[v]=0;
      return res;
    };
  
  int ans=0;
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      int v=idx(0,x,y);
      if(~dp[v]) chmax(ans,dfs(v));
      while(!qu.empty()){
	used[qu.front()]=1;
	visit[qu.front()]=2;
	qu.pop();
      }
    }
  }

  cout<<ans<<endl;
  return 0;
}
accuracy: 0.8163265306122449
time_ms: 110
memory_kb: 28468
final_rank: 13

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

//INSERT ABOVE HERE
signed main(){
  const int D = 11;
 
  int n,m;
  cin>>n>>m;
  vector<string> s(n);
  for(int i=0;i<n;i++) cin>>s[i];

  vector<vector<int> > E(n,vector<int>(n,0));
  for(int i=0;i<m;i++){
    int x,y;
    cin>>x>>y;
    x--;y--;
    E[x][y]=1;
  }

  vector<int> l(n);
  for(int i=0;i<n;i++) l[i]=s[i].size();


  using T = tuple<int, int, int>;
  auto idx=[&](int d,int x,int y){return (D+d)*n*n+x*n+y;};
  auto inv=[&](int v){return T(v/(n*n)-D,v%(n*n)/n,v%n);};
  int sz=idx(D,n-1,n-1)+1;

  queue<T> qt;
  vector<int> dp(sz,-1);
  
  // center inside s[x]
  for(int x=0;x<n;x++){
    // even
    for(int i=1;i<l[x];i++){
      // [0, i) + [i, l)
      int k=min(i,l[x]-i),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+j];
      if(ok){
	int d=i-(l[x]-i);
	qt.emplace(d,x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
    // odd
    for(int i=0;i<l[x];i++){
      // [0, i) + [i, i+1) + [i+1, l)
      int k=min(i,l[x]-(i+1)),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+1+j];
      if(ok){
	int d=i-(l[x]-(i+1));
	qt.emplace(i-(l[x]-(i+1)),x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
  }

  // s[x] |center| s[y] (obviously even)
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      if(!E[x][y]) continue;
      int k=min(l[x],l[y]),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][l[x]-(j+1)]==s[y][j];
      if(ok){
	int d=l[x]-l[y];
	qt.emplace(d,x,y);
	chmax(dp[idx(d,x,x)],l[x]+l[y]);
      }
    }    
  }

  using P = pair<int, int>;
  vector<vector<P> > G(sz),R(sz);
  
  for(int d=-D;d<=D;d++){
    for(int x=0;x<n;x++){
      for(int y=0;y<n;y++){
	int v=idx(d,x,y);
	//cout<<v<<":"<<d<<" "<<x<<" "<<y<<endl;
	for(int i=0;i<n;i++){
	  // s[i] s[x] s[y]
	  if(d<=0&&E[i][x]){
	    int k=min(-d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][l[i]-(j+1)]==s[y][l[y]+d+j];
	    if(ok){	     
	      int nd=d+l[i];
	      int u=idx(nd,i,y);
	      //cout<<v<<" -> "<<u<<endl;
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	  // s[x] s[y] s[i]
	  if(d>=0&&E[y][i]){
	    int k=min(d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][j]==s[x][d-(j+1)];
	    if(ok){
	      int nd=d-l[i];
	      int u=idx(nd,x,i);
	      //cout<<v<<" -> "<<u<<endl;
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	}
      }           
    }
  }

  // find nodes that can reached from starts
  while(!qt.empty()){
    int d,x,y;
    tie(d,x,y)=qt.front();qt.pop();
    int v=idx(d,x,y);
    //cout<<dp[v]<<":"<<d<<" "<<x<<" "<<y<<endl;
    for(auto e:G[v]){
      int u,c;
      tie(u,c)=e;
      if(~dp[u]) continue;
      chmax(dp[u],dp[v]+c);
      qt.emplace(inv(u));      
    }    
  }

  vector<int> dp2(sz,-1),used(sz,0),visit(sz,-1);
  queue<int> qu;
  int pos=0;
  function<int(int)> dfs=
    [&](int v){
      int &res=dp2[v];
      if(used[v]) return res;
      //cout<<v<<"****"<<endl;
      //cout<<get<0>(inv(v))<<":"<<get<1>(inv(v))<<":"<<get<2>(inv(v))<<endl;
      
      visit[v]=pos++;
      qu.emplace(v);
      res=dp[v];
      for(auto e:R[v]){
	int u,c;
	tie(u,c)=e;
	if(dp[u]<0) continue;
	//cout<<setw(6)<<v<<":"<<setw(3)<<dp[v]<<":"<<setw(3)<<visit[v]<<endl;
	//cout<<setw(6)<<u<<"+"<<setw(3)<<dp[u]<<"+"<<setw(3)<<visit[u]<<endl;
	if(~visit[u]&&visit[u]<=visit[v]){
	  cout<<-1<<endl;
	  exit(0);
	}
	chmax(res,dfs(u)+c);
      }
      return res;
    };
  
  int ans=0;
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      int v=idx(0,x,y);
      if(~dp[v]) chmax(ans,dfs(v));
      while(!qu.empty()){
	used[qu.front()]=1;
	visit[qu.front()]=sz+1;
	qu.pop();
      }
    }
  }

  cout<<ans<<endl;
  return 0;
}
accuracy: 0.6632653061224489
time_ms: 100
memory_kb: 28464
final_rank: 14

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

//INSERT ABOVE HERE
signed main(){
  const int D = 11;
 
  int n,m;
  cin>>n>>m;
  vector<string> s(n);
  for(int i=0;i<n;i++) cin>>s[i];

  vector<vector<int> > E(n,vector<int>(n,0));
  for(int i=0;i<m;i++){
    int x,y;
    cin>>x>>y;
    x--;y--;
    E[x][y]=1;
  }

  vector<int> l(n);
  for(int i=0;i<n;i++) l[i]=s[i].size();


  using T = tuple<int, int, int>;
  auto idx=[&](int d,int x,int y){return (D+d)*n*n+x*n+y;};
  auto inv=[&](int v){return T(v/(n*n)-D,v%(n*n)/n,v%n);};
  int sz=idx(D,n-1,n-1)+1;

  queue<T> qt;
  vector<int> dp(sz,-1);
  
  // center inside s[x]
  for(int x=0;x<n;x++){
    // even
    for(int i=1;i<l[x];i++){
      // [0, i) + [i, l)
      int k=min(i,l[x]-i),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+j];
      if(ok){
	int d=i-(l[x]-i);
	qt.emplace(d,x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
    // odd
    for(int i=0;i<l[x];i++){
      // [0, i) + [i, i+1) + [i+1, l)
      int k=min(i,l[x]-(i+1)),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+1+j];
      if(ok){
	int d=i-(l[x]-(i+1));
	qt.emplace(i-(l[x]-(i+1)),x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
  }

  // s[x] |center| s[y] (obviously even)
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      if(!E[x][y]) continue;
      int k=min(l[x],l[y]),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][l[x]-(j+1)]==s[y][j];
      if(ok){
	int d=l[x]-l[y];
	qt.emplace(d,x,y);
	chmax(dp[idx(d,x,x)],l[x]+l[y]);
      }
    }    
  }

  using P = pair<int, int>;
  vector<vector<P> > G(sz),R(sz);
  
  for(int d=-D;d<=D;d++){
    for(int x=0;x<n;x++){
      for(int y=0;y<n;y++){
	int v=idx(d,x,y);
	//cout<<v<<":"<<d<<" "<<x<<" "<<y<<endl;
	for(int i=0;i<n;i++){
	  // s[i] s[x] s[y]
	  if(d<=0&&E[i][x]){
	    int k=min(-d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][l[i]-(j+1)]==s[y][l[y]+d+j];
	    if(ok){	     
	      int nd=d+l[i];
	      int u=idx(nd,i,y);
	      //cout<<v<<" -> "<<u<<endl;
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	  // s[x] s[y] s[i]
	  if(d>=0&&E[y][i]){
	    int k=min(d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][j]==s[x][d-(j+1)];
	    if(ok){
	      int nd=d-l[i];
	      int u=idx(nd,x,i);
	      //cout<<v<<" -> "<<u<<endl;
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	}
      }           
    }
  }

  // find nodes that can reached from starts
  while(!qt.empty()){
    int d,x,y;
    tie(d,x,y)=qt.front();qt.pop();
    int v=idx(d,x,y);
    //cout<<dp[v]<<":"<<d<<" "<<x<<" "<<y<<endl;
    for(auto e:G[v]){
      int u,c;
      tie(u,c)=e;
      if(~dp[u]) continue;
      chmax(dp[u],dp[v]+c);
      qt.emplace(inv(u));      
    }    
  }

  vector<int> dp2(sz,-1),used(sz,0),visit(sz,-1);
  queue<int> qu;
  int pos=0;
  function<int(int)> dfs=
    [&](int v){
      int &res=dp2[v];
      if(used[v]) return res;
      //cout<<v<<"****"<<endl;
      //cout<<get<0>(inv(v))<<":"<<get<1>(inv(v))<<":"<<get<2>(inv(v))<<endl;
      
      visit[v]=pos++;
      qu.emplace(v);
      res=dp[v];
      for(auto e:R[v]){
	int u,c;
	tie(u,c)=e;
	//cout<<setw(6)<<v<<":"<<setw(3)<<dp[v]<<":"<<setw(3)<<visit[v]<<endl;
	//cout<<setw(6)<<u<<"+"<<setw(3)<<dp[u]<<"+"<<setw(3)<<visit[u]<<endl;
	if(dp[u]<0) continue;
	if(~visit[u]&&visit[u]<=visit[v]){
	  cout<<-1<<endl;
	  exit(0);
	}
	chmax(res,dfs(u)+c);
      }
      return res;
    };
  
  int ans=0;
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      int v=idx(0,x,y);
      if(~dp[v]) chmax(ans,dfs(v));
      while(!qu.empty()){
	used[qu.front()]=1;
	qu.pop();
      }
    }
  }

  cout<<ans<<endl;
  return 0;
}
accuracy: 0.5816326530612245
time_ms: 110
memory_kb: 24084
final_rank: 15

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

//INSERT ABOVE HERE
signed main(){
  const int D = 11;
 
  int n,m;
  cin>>n>>m;
  vector<string> s(n);
  for(int i=0;i<n;i++) cin>>s[i];

  vector<vector<int> > E(n,vector<int>(n,0));
  for(int i=0;i<m;i++){
    int x,y;
    cin>>x>>y;
    x--;y--;
    E[x][y]=1;
  }

  vector<int> l(n);
  for(int i=0;i<n;i++) l[i]=s[i].size();


  using T = tuple<int, int, int>;
  auto idx=[&](int d,int x,int y){return (D+d)*n*n+x*n+y;};
  auto inv=[&](int v){return T(v/(n*n)-D,v%(n*n)/n,v%n);};
  int sz=idx(D,n-1,n-1)+1;

  queue<T> qt;
  vector<int> dp(sz,-1);
  
  // center inside s[x]
  for(int x=0;x<n;x++){
    // even
    for(int i=1;i<l[x];i++){
      // [0, i) + [i, l)
      int k=min(i,l[x]-i),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+j];
      if(ok){
	int d=i-(l[x]-i);
	qt.emplace(d,x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
    // odd
    for(int i=0;i<l[x];i++){
      // [0, i) + [i, i+1) + [i+1, l)
      int k=min(i,l[x]-(i+1)),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][i-(j+1)]==s[x][i+1+j];
      if(ok){
	int d=i-(l[x]-(i+1));
	qt.emplace(i-(l[x]-(i+1)),x,x);
	chmax(dp[idx(d,x,x)],l[x]);
      }
    }
  }

  // s[x] |center| s[y] (obviously even)
  for(int x=0;x<n;x++){
    for(int y=0;y<n;y++){
      if(!E[x][y]) continue;
      int k=min(l[x],l[y]),ok=1;
      for(int j=0;j<k;j++)
	ok&=s[x][l[x]-(j+1)]==s[y][j];
      if(ok){
	int d=l[x]-l[y];
	qt.emplace(d,x,y);
	chmax(dp[idx(d,x,x)],l[x]+l[y]);
      }
    }    
  }

  using P = pair<int, int>;
  vector<vector<P> > G(sz),R(sz);
  
  for(int d=-D;d<=D;d++){
    for(int x=0;x<n;x++){
      for(int y=0;y<n;y++){
	if(!E[x][y]) continue;
	for(int i=0;i<n;i++){
	  // s[i] s[x] s[y]
	  if(d<=0&&E[i][x]){
	    int k=min(-d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][l[i]-(j+1)]==s[y][l[y]-d+j];
	    if(ok){
	      int nd=d+l[i];
	      int v=idx(d,x,y),u=idx(nd,i,y);
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	  // s[x] s[y] s[i]
	  if(d>=0&&E[y][i]){
	    int k=min(d,l[i]),ok=1;
	    for(int j=0;j<k;j++)
	      ok&=s[i][j]==s[x][d-(j+1)];
	    if(ok){
	      int nd=d-l[i];
	      int v=idx(d,x,y),u=idx(nd,x,i);
	      G[v].emplace_back(u,l[i]);
	      R[u].emplace_back(v,l[i]);	      
	    }
	  }
	}
      }           
    }
  }

  // find nodes that can reached from starts
  while(!qt.empty()){
    int d,x,y;
    tie(d,x,y)=qt.front();qt.pop();
    int v=idx(d,x,y);
    for(auto e:G[v]){
      int u,c;
      tie(u,c)=e;
      if(~dp[u]) continue;
      chmax(dp[u],dp[v]+c);
      qt.emplace(inv(u));      
    }    
  }

  vector<int> dp2(sz,-1),visit(sz,0);

  int pos=0;
  function<int(int)> dfs=
    [&](int v){
      int &res=dp2[v];
      if(~res) return res;
      visit[v]=pos++;
      res=dp[v];
      for(auto e:R[v]){
	int u,c;
	tie(u,c)=e;
	if(dp[u]<0) continue;
	if(visit[u]<=visit[v]){
	  cout<<-1<<endl;
	  exit(0);
	}
	chmax(res,dfs(u)+c);
      }
      return res;
    };
  
  int ans=0;
  for(int x=0;x<n;x++)
    for(int y=0;y<n;y++)
      if(~dp[idx(0,x,y)]&&dp2[idx(0,x,y)]<0)
	chmax(ans,dfs(idx(0,x,y)));

  cout<<ans<<endl;
  return 0;
}
accuracy: 0.30612244897959184
time_ms: 10
memory_kb: 17224
final_rank: 18

code_snippet: #include "bits/stdc++.h"
#include<unordered_map>
#include<unordered_set>
#pragma warning(disable:4996)
using namespace std;

int main()
{
	vector<char>chs(2000);
	vector<vector<int>>real_edges(2000);
	{
		int N, M; cin >> N >> M;
		vector<string>sts;
		for (int i = 0; i < N; ++i) {
			string st; cin >> st;
			sts.push_back(st);
		}
		vector<pair<int, int>>edges;
		for (int i = 0; i < M; ++i) {
			int a, b; cin >> a >> b;
			a--; b--;
			edges.push_back(make_pair(a, b));
		}
		int id = 0;
		vector<int>ids(N);
		for (int i = 0; i < N; ++i) {
			ids[i] = id;
			for (int j = 0; j < sts[i].size() - 1; ++j) {
				real_edges[ids[i] + j].push_back(ids[i] + j + 1);
			}
			for (int j = 0; j < sts[i].size(); ++j) {
				chs[ids[i] + j] = sts[i][j];
			}
			id += sts[i].size();
		}
		ids.push_back(id);
		for (auto e : edges) {
			real_edges[ids[e.first + 1] - 1].push_back(ids[e.second]);
		}
		const int start = id;
		const int goal = start + 1;

		for (int i = 0; i < N; ++i) {
			real_edges[start].push_back(ids[i]);
			real_edges[ids[i + 1] - 1].push_back(goal);
		}
		chs.resize(goal + 1);
		real_edges.resize(goal + 1);
	}
	const int N = real_edges.size();
	vector<vector<int>>rev_edges(N);

	for (int i = 0; i < N; ++i) {
		for (auto e : real_edges[i]) {
			rev_edges[e].push_back(i);
		}
	}

	vector<vector<int>>memo(N, vector<int>(N, -1));
	const int S = N - 2;
	const int G = S + 1;
	memo[S][G]=0;
	queue<pair<int,int>>que;
	que.push(make_pair(S,G));
	int ans=0;
	while (!que.empty()) {
		const auto atop(que.front());
		que.pop();
		const int a=atop.first;
		const int b=atop.second;

		if(memo[a][b]>8000)break;
	

		for (auto next_a : real_edges[a]) {
			for (auto next_b : rev_edges[b]) {
				if (chs[next_a] == chs[next_b]) {
					if (a == next_b&&b == next_a) {
						ans=max(ans,memo[a][b]);
						if (ans > 2010) {
							ans=-1;
							break;
						}
					}
					if (next_b == next_a) {
						ans=max(ans,memo[a][b]+1);
						if (ans > 2010) {
							ans = -1;
							break;
						}
					}
					if (memo[next_a][next_b] < memo[a][b]+2) {
						memo[next_a][next_b]=memo[a][b]+2;
						que.push(make_pair(next_a,next_b));
					}
				}
			}
		}
	}
	cout<<ans<<endl;

	return 0;
}
accuracy: 1
time_ms: 870
memory_kb: 19844
final_rank: 6

code_snippet: #include "bits/stdc++.h"
#include<unordered_map>
#include<unordered_set>
#pragma warning(disable:4996)
using namespace std;

int main()
{
	vector<char>chs(2000);
	vector<vector<int>>real_edges(2000);
	{
		int N, M; cin >> N >> M;
		vector<string>sts;
		for (int i = 0; i < N; ++i) {
			string st; cin >> st;
			sts.push_back(st);
		}
		vector<pair<int, int>>edges;
		for (int i = 0; i < M; ++i) {
			int a, b; cin >> a >> b;
			a--; b--;
			edges.push_back(make_pair(a, b));
		}
		int id = 0;
		vector<int>ids(N);
		for (int i = 0; i < N; ++i) {
			ids[i] = id;
			for (int j = 0; j < sts[i].size() - 1; ++j) {
				real_edges[ids[i] + j].push_back(ids[i] + j + 1);
			}
			for (int j = 0; j < sts[i].size(); ++j) {
				chs[ids[i] + j] = sts[i][j];
			}
			id += sts[i].size();
		}
		ids.push_back(id);
		for (auto e : edges) {
			real_edges[ids[e.first + 1] - 1].push_back(ids[e.second]);
		}
		const int start = id;
		const int goal = start + 1;

		for (int i = 0; i < N; ++i) {
			real_edges[start].push_back(ids[i]);
			real_edges[ids[i + 1] - 1].push_back(goal);
		}
		chs.resize(goal + 1);
		real_edges.resize(goal + 1);
	}
	const int N = real_edges.size();
	vector<vector<int>>rev_edges(N);

	for (int i = 0; i < N; ++i) {
		for (auto e : real_edges[i]) {
			rev_edges[e].push_back(i);
		}
	}

	vector<vector<int>>memo(N, vector<int>(N, -1));
	const int S = N - 2;
	const int G = S + 1;
	memo[S][G]=0;
	queue<pair<int,int>>que;
	que.push(make_pair(S,G));
	int ans=0;
	while (!que.empty()) {
		const auto atop(que.front());
		que.pop();
		const int a=atop.first;
		const int b=atop.second;

		if(memo[a][b]>4000)break;
	

		for (auto next_a : real_edges[a]) {
			for (auto next_b : rev_edges[b]) {
				if (chs[next_a] == chs[next_b]) {
					if (a == next_b&&b == next_a) {
						ans=max(ans,memo[a][b]);
						if (ans > 2010) {
							ans=-1;
							break;
						}
					}
					if (next_b == next_a) {
						ans=max(ans,memo[a][b]+1);
						if (ans > 2010) {
							ans = -1;
							break;
						}
					}
					if (memo[next_a][next_b] < memo[a][b]+2) {
						memo[next_a][next_b]=memo[a][b]+2;
						que.push(make_pair(next_a,next_b));
					}
				}
			}
		}
	}
	cout<<ans<<endl;

	return 0;
}
accuracy: 0.41836734693877553
time_ms: 10
memory_kb: 7184
final_rank: 17