#include <bits/stdc++.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/irange.hpp>
#include <boost/multiprecision/cpp_int.hpp>
using namespace std;
using boost::irange;
using boost::algorithm::join;

typedef long long ll;
typedef unsigned long long ull;
//typedef long double ld;

typedef map<int, int> mii;
typedef pair<int, int> pii;
typedef pair<double,double> pdd;
typedef set<int> si;
typedef set<ll> sll;
typedef vector<int> vi;
typedef vector<pair<int, int > > vii;
typedef vector<ll> vll;
typedef vector<double> vd;
typedef vector<vi> vvi;
typedef vector<vii> vvii;
typedef vector<vll> vvll;

constexpr ll MOD = 1e9+7;
template<typename T>constexpr auto inf=numeric_limits<T>::max()/4;
constexpr int INF=inf<int>;
constexpr ll LINF=inf<ll>;

#define Sort(x) sort(begin(x),end(x))
#define Reverse(x) reverse(begin(x),end(x))
#define ABS(a,b) ((a)<(b)?(b)-(a):(a)-(b))
#define nbit(n) (1ull<<(n))
#define _ol3(_1,_2,_3,name,...)name
#define _rep(i,n)_repi(i,0,n)
#define _repi(i,a,b)for(int i=a,i##_l=(b);i<i##_l;++i)
#define REP(...) _ol3(__VA_ARGS__,_repi,_rep,)(__VA_ARGS__)
#define REPR(i,n) for(int i=n-1;i>=0;--i)
#define REPA(i,v) REP(i,(v).size())

// sort(begin(x), end(x), greater<ll>());
// lower_bound(a.begin(), a.end(), val): a[i] ≥ valである最左の位置
// upper_bound(a.begin(), a.end(), val): a[i] > valである最左の位置

// Returns minimum of a and b.
// If a is less b, a is set to b.
template<typename T>
T chmin(T& a, T b) {
  if (a > b) {
    a = b;
  }
  return a;
}

// Returns maximum of a and b.
// If a is less b, a is set to b.
template<typename T>
T chmax(T& a, T b) {
  if (a < b) {
    a = b;
  }
  return a;
}

template <typename T>
void dumpContents(const T& v, const string& msg)
{
  cerr << "### " << msg << " ###\n";
  for (const auto& x : v) {
    cerr << x << " ";
  }
  cerr << endl;
}

struct before_main_function {
  before_main_function() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout<<setprecision(20)<<fixed;
    #define endl "\n"
  }
} before_main_function;

template<int MAX_N>
class Dijkstra {

public:
  struct Edge {
    int from;
    int to;
    ll cost;

    Edge(int from_, int to_, ll cost_)
      : from(from_), to(to_), cost(cost_) {}
  };

private:
  const int N;
  using P = pair<ll, int>; // (distance, node id)
  vector<vector<Edge> > edges;
  priority_queue<P, vector<P>, greater<P> > q;

public:
  vll d; // distance to Node i

  explicit Dijkstra(int N_) :
	  N(N_), d(N, LINF) {
    edges.resize(N);
  }

  // Add a directed edge.
  // For an undirected graph, the caller should add both directions.
  void add_edge(int from, int to, ll cost) {
    edges[from].emplace_back(from, to, cost);
  }

  void add_edge(const Edge& e) {
    edges[e.from].push_back(e);
  }

  void run(int start) {
    // Dijkstra
    // C++のpriority queueは値のupdateはできない
    // あるノードの距離が短く更新されたら、再度pushする
    // popするときに、確定済みであれば無視する

    q.emplace(0, start);
    d[start] = 0;
    //vector<bool> visited(N);

    while (!q.empty()) {
      P p = q.top(); q.pop();
      const int v = p.second;
      if (d[v] < p.first) continue;
      //if (visited[v]) continue;
      //visited[v] = true;

      for (const auto& e : edges[v]) {
        const ll new_cost = d[v] + e.cost;
        if (d[e.to] > new_cost) {
          d[e.to] = new_cost;
          q.emplace(new_cost, e.to); // 重複しているかもしれないpush
        }
      }
    }
  }

  // Return the distance from the root to i
  ll get_distance(int i) {
    return d[i];
  }
};
// ========== end of template ==========
constexpr int N_MAX=1e5;
constexpr int M_MAX=2e5;
Dijkstra<N_MAX+M_MAX*2+10> G(N_MAX+M_MAX*2+10);

int N, M;

vector<map<int, int> > stations(N_MAX);

void add_line(int p, int q, int c) {
  static int count = N_MAX;

  if (stations[p][c] == 0) {
    G.add_edge(p, count, 1);
    G.add_edge(count, p, 0);
    stations[p][c] = count++;
  }
  if (stations[q][c] == 0) {
    G.add_edge(q, count, 1);
    G.add_edge(count, q, 0);
    stations[q][c] = count++;
  }

  G.add_edge(stations[p][c], stations[q][c], 0);
  G.add_edge(stations[q][c], stations[p][c], 0);
}

int main(int argc, char** argv) {
  cin >> N >> M;

  // 0 .. N-1は元の駅
  // stations[i][j]は、元の駅iに併設した路線jの駅

  REP(i, M) {
    int p, q, c;
    cin >> p >> q >> c;
    p--; q--; c--;

    add_line(p, q, c);
  }

  G.run(0);
  int ret = G.get_distance(N-1);
  if (ret>=INF) ret = -1;
  cout << ret << endl;

  return 0;
}


Measured memory_bytes: 73844