#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 NUM 105

struct Edge{
	Edge(){
		s = t = 0;
	}
	Edge(int arg_s,int arg_t,ll arg_cost){
		s = arg_s;
		t = arg_t;
	}
	bool operator<(const struct Edge &arg) const{
		if(s != arg.s){
			return s < arg.s;
		}else{
			return t < arg.t;
		}
	}
	int s,t;
};

struct Info{
	Info(int arg_to,int arg_edge_id){
		to = arg_to;
		edge_id = arg_edge_id;
	}
	int to,edge_id;
};

struct Node{
	int weight;
};

int V,E,number;
int order[NUM],lowlink[NUM];
int height[NUM],parent[NUM],num_member[NUM];
int index_table[NUM];
int dp_UP[NUM][NUM],dp_DOWN[NUM][NUM],work[2][NUM][NUM];
int subtree_size[NUM];
ll sum_weight[NUM];
bool visited[NUM],is_bridge[NUM];
string keywords[NUM];
Edge edge[NUM*NUM];
vector<Info> G[NUM];
vector<int> Group_G[NUM];
Node nodes[NUM];

int find(int x){
	if(x == parent[x])return x;
	else{
		return parent[x] = find(parent[x]);
	}
}

bool isSame(int x,int y){
	return find(x) == find(y);
}

void unite(int x,int y){
	x = find(x);
	y = find(y);

	if(x == y)return;

	if(height[x] < height[y]){
		num_member[y] += num_member[x];
		parent[x] = y;
	}else{
		num_member[x] += num_member[y];
		parent[y] = x;
		if(height[x] == height[y])height[x]++;
	}
}

void recursive(int node_id){

	order[node_id] = number++;
	lowlink[node_id] = order[node_id];

	int next_node;

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

		if(order[next_node] == -1){

			visited[G[node_id][i].edge_id] = true;
			recursive(next_node);
			lowlink[node_id] = min(lowlink[node_id],lowlink[next_node]);

		}else if(visited[G[node_id][i].edge_id] == false){
			lowlink[node_id] = min(lowlink[node_id],order[next_node]);
		}
	}
}

void dfs(int node_id,int parent){

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

		if(Group_G[node_id][i] == parent)continue;

		dfs(Group_G[node_id][i],node_id);
	}

	int CURRENT = 0,NEXT = 1;

	for(int i = 0; i < 2; i++){
		for(int a = 0; a <= V; a++){
			for(int b = 0; b <= V; b++){
				work[i][a][b] = -BIG_NUM;
			}
		}
	}

	work[CURRENT][0][0] = 0;

	int child;

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

		child = Group_G[node_id][i];
		if(child == parent)continue;

		for(int a = 0; a <= subtree_size[node_id]; a++){
			for(int b = 0; b <= subtree_size[node_id]; b++){

				for(int c = 0; c <= subtree_size[child]; c++){
					work[NEXT][a+c][b] = max(work[NEXT][a+c][b],work[CURRENT][a][b]+dp_UP[child][c]);
					work[NEXT][a][b+c] = max(work[NEXT][a][b+c],work[CURRENT][a][b]+dp_DOWN[child][c]);
				}
			}
		}

		subtree_size[node_id] += subtree_size[child];

		swap(CURRENT,NEXT);

		for(int a = 0; a <= V; a++){
			for(int b = 0; b <= V; b++){
				work[NEXT][a][b] = -BIG_NUM;
			}
		}
	}

	for(int a = 0; a <= subtree_size[node_id]; a++){
		for(int b = 0; b <= subtree_size[node_id]; b++){
			dp_UP[node_id][a+nodes[node_id].weight] = max(dp_UP[node_id][a+nodes[node_id].weight],
					work[CURRENT][a][b]+(a+nodes[node_id].weight)*(b+nodes[node_id].weight));

			dp_DOWN[node_id][b+nodes[node_id].weight] = max(dp_DOWN[node_id][b+nodes[node_id].weight],
								work[CURRENT][a][b]+(a+nodes[node_id].weight)*(b+nodes[node_id].weight));
		}
	}

	subtree_size[node_id] += nodes[node_id].weight;
}


int main(){

	cin >> V >> E;

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

		cin >> keywords[i];
	}

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

		string A,B;
		int from,to;

		cin >> A >> B;

		for(int k = 0; k < V; k++){
			if(keywords[k] == A){
				from = k;
				break;
			}
		}

		for(int k = 0; k < V; k++){
			if(keywords[k] == B){
				to = k;
				break;
			}
		}

		edge[i].s = from;
		edge[i].t = to;

		if(edge[i].s > edge[i].t){
			swap(edge[i].s,edge[i].t);
		}

		G[edge[i].s].push_back(Info(edge[i].t,i));
		G[edge[i].t].push_back(Info(edge[i].s,i));
	}

	for(int i = 0; i < V; i++){
		order[i] = -1;
		lowlink[i] = -1;
	}

	for(int i = 0; i < E; i++){
		visited[i] = false;
	}

	recursive(0);

	int from,to;

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

		is_bridge[i] = false;
	}

	int num_bridge = 0;

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

		from = edge[i].s;
		to = edge[i].t;

		if(order[edge[i].s] > order[edge[i].t]){
			swap(from,to);
		}

		if(order[from] < lowlink[to]){
			num_bridge++;
			is_bridge[i] = true;
		}
	}

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

		parent[i] = i;
		height[i] = 0;
		num_member[i] = 1;
	}

	for(int i = 0; i < E; i++){
		if(is_bridge[i]){
			continue;
		}

		unite(edge[i].s,edge[i].t);
	}

	int num_boss = 0;

	for(int i = 0; i < V; i++){
		if(find(i) != i)continue;

		index_table[i] = num_boss++;
		nodes[index_table[i]].weight = num_member[i];
	}

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

		if(isSame(edge[i].s,edge[i].t)){
			continue;
		}

		Group_G[index_table[find(edge[i].s)]].push_back(index_table[find(edge[i].t)]);
		Group_G[index_table[find(edge[i].t)]].push_back(index_table[find(edge[i].s)]);
	}

	int num_nodes = num_boss;

	for(int i = 0; i < num_boss; i++){

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

	int root = 0;

	for(int i = 0; i < num_nodes; i++){
		for(int k = 0; k <= V; k++){
			dp_UP[i][k] = 0;
			dp_DOWN[i][k] = 0;
		}
	}

	for(int i = 0; i < num_nodes; i++){
		subtree_size[i] = 0;
	}

	dfs(root,-1);

	int ans = 0;

	for(int i = 0; i <= V; i++){
		ans = max(ans,max(dp_UP[root][i],dp_DOWN[root][i]));
	}

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

	return 0;
}



Measured memory_bytes: 3600