#include <bits/stdc++.h>
using namespace std;
int n, m;
int s, t;
int depth[200005];
int treeEdgeFromParent[200005];
int parent[200005];
struct edgeType {
  int to, nxt;
  bool treeEdge, flag;
  int nonTreeEdgeCover;
} e[200005 << 1];
int head[200005];
bool flag[200005];
int qhy[200005 << 1];
int open;
int cnt;
int zyg[200005];
int closed;
void add_edge(int u, int v, int x) {
  e[x].to = v;
  e[x].nxt = head[u];
  head[u] = x;
}
void standing_by() {
  int i, u, v;
  scanf("%d%d", &n, &m);
  for (i = 1; i <= m; i++) {
    scanf("%d%d", &u, &v);
    add_edge(u, v, i * 2 - 1);
    add_edge(v, u, i * 2);
  }
}
void dfs(int x, int p) {
  int i;
  flag[x] = true;
  depth[x] = depth[p] + 1;
  for (i = head[x]; i; i = e[i].nxt) {
    if (!e[i].flag) {
      e[i].flag = e[(i & 1 ? i + 1 : i - 1)].flag = true;
      qhy[++open] = (i + 1) >> 1;
    }
    if (!flag[e[i].to]) {
      e[i].treeEdge = e[(i & 1 ? i + 1 : i - 1)].treeEdge = true;
      treeEdgeFromParent[e[i].to] = (i + 1) >> 1;
      parent[e[i].to] = x;
      dfs(e[i].to, x);
    }
  }
}
void get_ans(int x, int p, int u, int v) {
  int i;
  if (x != s) qhy[++open] = x;
  if (x == t) {
    printf("%d ", open);
    for (i = 1; i < open; i++) printf("%d ", qhy[i]);
    printf("%d\n", qhy[i]);
    return;
  }
  for (i = head[x]; i; i = e[i].nxt)
    if (e[i].to != p &&
        (e[i].nonTreeEdgeCover == u || e[i].nonTreeEdgeCover == v)) {
      if (x == s) {
        if (cnt == 3) return;
        qhy[open = 1] = s;
        cnt++;
      }
      get_ans(e[i].to, x, u, v);
    }
}
void complete() {
  int i, j;
  int u, v;
  int k;
  bool rev;
  for (i = 1; i <= n; i++)
    if (!flag[i]) {
      open = 0;
      dfs(i, 0);
      for (j = 1; j <= open; j++) {
        if (e[qhy[j] * 2].treeEdge) continue;
        e[qhy[j] * 2 - 1].nonTreeEdgeCover = e[qhy[j] * 2].nonTreeEdgeCover =
            qhy[j];
        u = e[qhy[j] * 2 - 1].to;
        v = e[qhy[j] * 2].to;
        k = 0;
        closed = 0;
        while (u != v) {
          if (depth[u] < depth[v]) u ^= v ^= u ^= v;
          zyg[++closed] = u;
          if (e[treeEdgeFromParent[u] * 2 - 1].nonTreeEdgeCover) {
            if (k == 0) k = e[treeEdgeFromParent[u] * 2 - 1].nonTreeEdgeCover;
            if (k != e[treeEdgeFromParent[u] * 2 - 1].nonTreeEdgeCover) {
              e[treeEdgeFromParent[u] * 2 - 1].nonTreeEdgeCover =
                  e[treeEdgeFromParent[u] * 2].nonTreeEdgeCover = qhy[j];
            }
          } else {
            e[treeEdgeFromParent[u] * 2 - 1].nonTreeEdgeCover =
                e[treeEdgeFromParent[u] * 2].nonTreeEdgeCover = qhy[j];
          }
          u = parent[u];
        }
        zyg[++closed] = u;
        if (k) {
          u = k;
          v = qhy[j];
          bool flag1, flag2;
          for (j = 1; j <= closed; j++) {
            flag1 = flag2 = false;
            for (k = head[zyg[j]]; k; k = e[k].nxt)
              if (e[k].nonTreeEdgeCover == u)
                flag1 = true;
              else if (e[k].nonTreeEdgeCover == v)
                flag2 = true;
            if (flag1 && flag2) {
              if (s == 0)
                s = zyg[j];
              else
                t = zyg[j];
            }
          }
          puts("YES");
          get_ans(s, 0, u, v);
          return;
        }
      }
    }
  puts("NO");
}
int main() {
  standing_by();
  complete();
  return 0;
}
