#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N;
long long R;
if (!(cin >> N >> R)) return 0;
vector<vector<int>> adj(N+1);
vector<vector<char>> adjMat(N+1, vector<char>(N+1, 0));
for (long long i = 0; i < R; ++i) {
int a,b; cin >> a >> b;
if (!adjMat[a][b]) {
adj[a].push_back(b);
adj[b].push_back(a);
adjMat[a][b] = adjMat[b][a] = 1;
}
}
if (N < 4) {
cout << "NO\n";
return 0;
}
// For each vertex v, try to find an induced cycle passing through v
vector<char> isNeighbor(N+1);
for (int v = 1; v <= N; ++v) {
int deg = (int)adj[v].size();
if (deg < 2) continue;
// mark neighbors
fill(isNeighbor.begin(), isNeighbor.end(), 0);
for (int u : adj[v]) isNeighbor[u] = 1;
// multi-source BFS from each neighbor u of v
const int UNVIS = -1;
vector<int> dist(N+1, UNVIS);
vector<int> src(N+1, -1);
vector<int> prev(N+1, -1);
queue<int> q;
for (int u : adj[v]) {
dist[u] = 0;
src[u] = u;
prev[u] = -1;
q.push(u);
}
bool found = false;
int meet_u = -1, meet_w = -1; // meet at node w, between source src[w] and current src[cur]
int meet_cur = -1;
while (!q.empty() && !found) {
int cur = q.front(); q.pop();
for (int to : adj[cur]) {
if (to == v) continue; // removed v from graph
// don't pass through other neighbors of v (they may be start nodes only)
if (isNeighbor[to] && src[cur] != to) {
// can visit 'to' only if it's the same source (i.e., starting node), else skip
continue;
}
if (dist[to] == UNVIS) {
dist[to] = dist[cur] + 1;
src[to] = src[cur];
prev[to] = cur;
q.push(to);
} else if (src[to] != src[cur]) {
// found meeting between two different sources: src[cur] and src[to]
int a = src[cur];
int b = src[to];
int L_edges = dist[cur] + 1 + dist[to];
if (L_edges >= 2) { // means cycle length >= 4 when add v
// reconstruct path a ... cur -to ... b
vector<int> path1; // a -> ... -> cur
int x = cur;
while (x != -1) { path1.push_back(x); if (x == a) break; x = prev[x]; }
reverse(path1.begin(), path1.end()); // from a to cur
vector<int> path2; // to -> ... -> b
x = to;
while (x != -1) { path2.push_back(x); if (x == b) break; x = prev[x]; }
// path2 is from 'to' to 'b' (forward)
// final combined cycle: v, path1 (a..cur), to, path2_tail (nodes after 'to' towards b)
vector<int> cycle;
cycle.push_back(v);
for (int node : path1) cycle.push_back(node);
// we are currently at 'cur' then edge cur->to, so add 'to'
cycle.push_back(to);
// append remaining of path2 after 'to' (excluding the first element 'to')
for (size_t idx = 1; idx < path2.size(); ++idx) cycle.push_back(path2[idx]);
// remove possible duplicates (shouldn't exist) and check length
// verify cycle size >=4
if ((int)cycle.size() >= 4) {
// Optional sanity checks (edges and induced) could be omitted for speed
for (size_t i = 0; i < cycle.size(); ++i) {
if (i) cout << ' ';
cout << cycle[i];
}
cout << '\n';
return 0;
}
}
}
}
}
}
cout << "NO\n";
return 0;
}