#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 1005;
// Adjacency list for graph traversal
vector<int> adj[MAXN];
// Adjacency matrix for O(1) edge existence checks
bool adjMat[MAXN][MAXN];
// Array to store which neighbor of the start_node is the ancestor of the current node
// 0 implies unvisited in the current BFS run
int root_neighbor[MAXN];
// Array to reconstruct the path (stores predecessor)
int parent[MAXN];
void solve() {
int N, R;
if (!(cin >> N >> R)) return;
// Clear graph data
for (int i = 0; i <= N; ++i) {
adj[i].clear();
for (int j = 0; j <= N; ++j) adjMat[i][j] = false;
}
// Read Input
for (int i = 0; i < R; ++i) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
adjMat[u][v] = adjMat[v][u] = true;
}
// Iterate through each node considering it as the "start" (S) of a potential cycle
for (int start_node = 1; start_node <= N; ++start_node) {
// Reset the visited/root tracking array for this iteration
// Using memset is efficient (O(N))
memset(root_neighbor, 0, sizeof(int) * (N + 1));
queue<int> q;
// Mark start_node as visited to prevent traversing back to it
root_neighbor[start_node] = -1;
// Initialize BFS with all neighbors of start_node
for (int neighbor : adj[start_node]) {
root_neighbor[neighbor] = neighbor; // The neighbor is the root of its branch
parent[neighbor] = start_node;
q.push(neighbor);
}
// Standard BFS
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : adj[u]) {
if (v == start_node) continue;
if (root_neighbor[v] == 0) {
// Node v is unvisited
root_neighbor[v] = root_neighbor[u];
parent[v] = u;
q.push(v);
} else {
// Node v is already visited. Check for collision.
// We only care if they come from different branches (different neighbors of start_node)
int r1 = root_neighbor[u];
int r2 = root_neighbor[v];
if (r1 != -1 && r2 != -1 && r1 != r2) {
// Collision between two different branches.
// Check if the roots (immediate neighbors of start_node) are connected.
// If (r1, r2) exists, then S-r1-r2-S is a triangle. We need a cycle >= 4.
// If they are NOT connected, we found a valid chordless cycle involving S.
if (!adjMat[r1][r2]) {
// Valid cycle found!
// Reconstruct the path.
// The cycle consists of: start_node -> ... -> u -> v -> ... -> start_node
vector<int> cycle;
cycle.push_back(start_node);
// 1. Path from r1 to u (Left side of the loop)
vector<int> path_left;
int curr = u;
while (curr != start_node) {
path_left.push_back(curr);
curr = parent[curr];
}
// path_left is [u, ..., r1]. We need it in order [r1, ..., u].
for (int k = path_left.size() - 1; k >= 0; --k) {
cycle.push_back(path_left[k]);
}
// 2. Path from v to r2 (Right side of the loop)
// We traverse v -> ... -> r2. Since the cycle continues u -> v,
// we append v and go up the parents towards r2.
curr = v;
while (curr != start_node) {
cycle.push_back(curr);
curr = parent[curr];
}
// Print the result
for (int k = 0; k < cycle.size(); ++k) {
cout << cycle[k] << (k == cycle.size() - 1 ? "" : " ");
}
cout << endl;
return; // Solution found, exit immediately
}
}
}
}
}
}
// If no such cycle is found after checking all start nodes
cout << "no" << endl;
}
int main() {
// Optimize I/O operations for performance
ios_base::sync_with_stdio(false);
cin.tie(NULL);
solve();
return 0;
}