Problem:
Score : 500 points

Problem Statement

Ken loves ken-ken-pa (Japanese version of hopscotch). Today, he will play it on a directed graph G.
G consists of N vertices numbered 1 to N, and M edges. The i-th edge points from Vertex u_i to Vertex v_i.

First, Ken stands on Vertex S. He wants to reach Vertex T by repeating ken-ken-pa. In one ken-ken-pa, he does the following exactly three times: follow an edge pointing from the vertex on which he is standing.

Determine if he can reach Vertex T by repeating ken-ken-pa. If the answer is yes, find the minimum number of ken-ken-pa needed to reach Vertex T. Note that visiting Vertex T in the middle of a ken-ken-pa does not count as reaching Vertex T by repeating ken-ken-pa.

Constraints

2 \leq N \leq 10^5

0 \leq M \leq \min(10^5, N (N-1))

1 \leq u_i, v_i \leq N(1 \leq i \leq M)

u_i \neq v_i (1 \leq i \leq M)

If i \neq j, (u_i, v_i) \neq (u_j, v_j).

1 \leq S, T \leq N

S \neq T

Input

Input is given from Standard Input in the following format:

N M
u_1 v_1
:
u_M v_M
S T

Output

If Ken cannot reach Vertex T from Vertex S by repeating ken-ken-pa, print -1.
If he can, print the minimum number of ken-ken-pa needed to reach vertex T.

Sample Input 1

4 4
1 2
2 3
3 4
4 1
1 3

Sample Output 1

2

Ken can reach Vertex 3 from Vertex 1 in two ken-ken-pa, as follows: 1 \rightarrow 2 \rightarrow 3 \rightarrow 4 in the first ken-ken-pa, then 4 \rightarrow 1 \rightarrow 2 \rightarrow 3 in the second ken-ken-pa. This is the minimum number of ken-ken-pa needed.

Sample Input 2

3 3
1 2
2 3
3 1
1 2

Sample Output 2

-1

Any number of ken-ken-pa will bring Ken back to Vertex 1, so he cannot reach Vertex 2, though he can pass through it in the middle of a ken-ken-pa.

Sample Input 3

2 0
1 2

Sample Output 3

-1

Vertex S and Vertex T may be disconnected.

Sample Input 4

6 8
1 2
2 3
3 4
4 5
5 1
1 4
1 5
4 6
1 6

Sample Output 4

2

Solution in Rust (1.42.0) (judge verdict: Accepted)

#![allow(unused_imports)]

// language: Rust(1.42.0)
// check available crates on AtCoder at "https://atcoder.jp/contests/language-test-202001"
// My Library Repositry is at "https://github.com/Loptall/sfcpl"

use itertools::*;
use itertools_num::*;
use lazy_static::lazy_static;
use maplit::{btreemap, btreeset, hashmap, hashset};
use num_bigint::{BigInt, BigUint};
use num_complex::Complex;
use num_integer::{binomial, gcd, lcm, multinomial, Integer};
use num_rational::Rational;
use num_traits::{
    clamp, one, pow, zero, Num, NumAssignOps, NumOps, One, Pow, Signed, Unsigned, Zero,
};
use permutohedron::Heap;
use proconio::{
    derive_readable, fastout, input, is_stdin_empty,
    marker::{Bytes, Chars, Isize1, Usize1},
};
use rand::random;
use std::cmp::{
    max, min, Ordering,
    Ordering::{Equal, Greater, Less},
    Reverse,
};
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
use std::convert::TryInto;
use std::fmt;
use std::mem::swap;
use std::num::{NonZeroU32, ParseIntError};
use std::ops::{
    Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
    Index, IndexMut, Mul, MulAssign, Neg, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub,
    SubAssign,
};
use std::process::exit;
use std::{f32, f64, i128, i16, i32, i64, i8, isize, u128, u16, u32, u64, u8, usize};

pub use visualize::*;
pub mod visualize {
    use itertools::*;
    pub trait Visualize {
        fn visualize(&self, split: &str);
        fn continuous(&self) {
            self.visualize("");
        }
        fn spaces(&self) {
            self.visualize(" ");
        }
        fn lines(&self) {
            self.visualize("\n");
        }
    }
    macro_rules ! impl_vis_for_sized {($ ($ t : ty ) ,+ ) => {$ (impl Visualize for $ t {fn visualize (& self , _split : & str ) {print ! ("{}" , self ) ; } } ) + } ; }
    impl_vis_for_sized! {usize , u8 , u16 , u32 , u64 , u128 , isize , i8 , i16 , i32 , i64 , i128 , String , & str , char }
    impl<T: std::fmt::Display> Visualize for [T] {
        fn visualize(&self, split: &str) {
            print!("{}", self.iter().join(split));
        }
    }
    #[macro_export]
    macro_rules ! vis {() => {println ! () ; } ; ($ last : expr ; ) => {$ last . lines () ; vis ! () } ; ($ last : expr => ) => {$ last . continuous () ; vis ! () ; } ; ($ last : expr $ (, ) ? ) => {$ last . spaces () ; vis ! () ; } ; ($ first : expr ; $ ($ rest : tt ) * ) => {$ first . lines () ; println ! () ; vis ! ($ ($ rest ) * ) ; } ; ($ first : expr => $ ($ rest : tt ) * ) => {$ first . continuous () ; vis ! ($ ($ rest ) * ) ; } ; ($ first : expr , $ ($ rest : tt ) * ) => {$ first . spaces () ; print ! (" " ) ; vis ! ($ ($ rest ) * ) ; } ; }
}

pub use consts::*;
pub mod consts {
    pub const MOD10E9_7: usize = 1000000007; // 10 ^ 9 + 7
    pub const MOD99_: usize = 998244353;
    pub const MAX: usize = std::usize::MAX; // = 2 ^ 64 - 1 = 18446744073709551615 ≈ 1.8 * 10 ^ 19
    pub const INF: usize = 2000000000000000000; // MAX / 9 < 2 * 10e18 < MAX / 10
    pub const FNI: i64 = -2000000000000000000; // == -(INF as i64)
    pub const PI: f64 = std::f64::consts::PI; // 3.141592653589793 -- 10 ^ -15
    pub const ASCII_A_LARGE: u8 = 65;
    pub const ASCII_A_SMALL: u8 = 97;
    pub const ASCII_0: u8 = 48;
    pub const ADJ4: &[(isize, isize); 4] = &[(1, 0), (0, 1), (-1, 0), (0, -1)];
    pub const ADJ8: &[(isize, isize); 8] = &[
        (1, 0),
        (1, 1),
        (0, 1),
        (-1, 1),
        (-1, 0),
        (-1, -1),
        (0, -1),
        (1, -1),
    ];
}

// snippet here
pub struct Bfs<'a, G: Graph<'a>> {
    visited: Vec<bool>,
    q: VecDeque<(G::NodeId, Option<G::NodeId>)>,
    g: &'a G,
}
impl<'a, G: Graph<'a>> Iterator for Bfs<'a, G> {
    type Item = (G::NodeId, G::NodeId);
    fn next(&mut self) -> Option<Self::Item> {
        if let Some((u, prev)) = self.q.pop_front() {
            for v in self.g.neighbors(u) {
                if !self.visited[self.g.index(v)] {
                    self.visited[self.g.index(v)] = true;
                    self.q.push_back((v, Some(u)));
                }
            }
            if let Some(prev) = prev {
                Some((prev, u))
            } else {
                self.next()
            }
        } else {
            None
        }
    }
}
/// `start`の頂点からの幅優先探索時の、
/// 頂点から頂点の接続をイテレートするIteratorを作る
/// ```rust
/// use sfcpl::graph::{util::make_undirected_graph, bfs::bfs};
/// let e = &[(0, 2), (0, 1), (1, 2), (2, 3), (3, 4), (3, 5), (1, 5)];
/// let g = make_undirected_graph(6, e);
/// for (f, t) in bfs(&g, 0) {
///     println!("{} -> {}", f, t);
/// }
/// ```
/// これは
/// `0 -> 2`
/// `0 -> 1`
/// `2 -> 3`
/// `1 -> 5`
/// `3 -> 4`
/// を表示する
pub fn bfs<'a, G>(g: &'a G, start: G::NodeId) -> Bfs<'a, G>
where
    G: Graph<'a, NodeId = usize>,
{
    let n = g.len();
    let mut visited = vec![false; n];
    let mut q = VecDeque::new();
    visited[start] = true;
    q.push_back((start, None));
    Bfs { visited, q, g }
}
pub trait Graph<'a> {
    type NodeId: Copy;
    type Iter: Iterator<Item = Self::NodeId>;
    fn len(&self) -> usize;
    fn is_empty(&self) -> bool;
    fn index(&self, i: Self::NodeId) -> usize;
    fn neighbors(&'a self, i: Self::NodeId) -> Self::Iter;
}
/// 重みなしグラフ
pub struct UnweightedGraph(Vec<Vec<usize>>);
impl UnweightedGraph {
    pub fn new(graph: Vec<Vec<usize>>) -> Self {
        Self(graph)
    }
}
impl<'a> Graph<'a> for UnweightedGraph {
    type NodeId = usize;
    type Iter = std::iter::Cloned<std::slice::Iter<'a, Self::NodeId>>;
    fn len(&self) -> usize {
        self.0.len()
    }
    fn is_empty(&self) -> bool {
        self.len() == 0
    }
    fn index(&self, a: Self::NodeId) -> usize {
        a
    }
    fn neighbors(&'a self, a: Self::NodeId) -> Self::Iter {
        self.0[a].iter().cloned()
    }
}
impl<'a> UnweightedGraph {
    pub fn dfs(&'a self, start: usize) -> Dfs<'a, UnweightedGraph> {
        dfs(self, start)
    }
    pub fn bfs(&'a self, start: usize) -> Bfs<'a, UnweightedGraph> {
        bfs(self, start)
    }
}
/// 重みありグラフ
pub struct WeightedNodeGraph<W>(Vec<Vec<(usize, W)>>);
impl<W> WeightedNodeGraph<W> {
    pub fn new(graph: Vec<Vec<(usize, W)>>) -> Self {
        Self(graph)
    }
}
impl<'a, W> Graph<'a> for WeightedNodeGraph<W>
where
    W: std::marker::Copy + Clone + 'a,
{
    type NodeId = (usize, W);
    type Iter = std::iter::Cloned<std::slice::Iter<'a, Self::NodeId>>;
    fn len(&self) -> usize {
        self.0.len()
    }
    fn is_empty(&self) -> bool {
        self.len() == 0
    }
    fn index(&self, a: Self::NodeId) -> usize {
        a.0
    }
    fn neighbors(&'a self, a: Self::NodeId) -> Self::Iter {
        self.0[a.0].iter().cloned()
    }
}
pub struct Dfs<'a, G: Graph<'a>> {
    visited: Vec<bool>,
    s: Vec<(G::NodeId, Option<G::NodeId>)>,
    g: &'a G,
}
impl<'a, G: Graph<'a>> Iterator for Dfs<'a, G> {
    type Item = (G::NodeId, G::NodeId);
    fn next(&mut self) -> Option<Self::Item> {
        if let Some((u, prev)) = self.s.pop() {
            for v in self.g.neighbors(u) {
                if !self.visited[self.g.index(v)] {
                    self.visited[self.g.index(v)] = true;
                    self.s.push((v, Some(u)));
                }
            }
            if let Some(prev) = prev {
                Some((prev, u))
            } else {
                self.next()
            }
        } else {
            None
        }
    }
}
/// `start`からの深さ優先探索時の、
/// 頂点から頂点の接続をイテレートするIteratorを作る
/// ```rust
/// use sfcpl::graph::{util::make_undirected_graph, dfs::dfs};
/// let e = &[(0, 2), (0, 1), (1, 2), (2, 3), (3, 4), (3, 5), (1, 5)];
/// let g = make_undirected_graph(6, e);
/// for (f, t) in dfs(&g, 0) {
///     println!("{} -> {}", f, t);
/// }
/// ```
/// これは
/// `0 -> 1`
/// `1 -> 5`
/// `5 -> 3`
/// `3 -> 4`
/// `0 -> 2`
/// を表示する
pub fn dfs<'a, G: Graph<'a, NodeId = usize>>(g: &'a G, start: G::NodeId) -> Dfs<'a, G> {
    let n = g.len();
    let mut visited = vec![false; n];
    let mut s = Vec::new();
    visited[start] = true;
    s.push((start, None));
    Dfs { visited, s, g }
}
/// 重みなし有向グラフ
pub fn make_directed_graph(n: usize, edges: &[(usize, usize)]) -> UnweightedGraph {
    let mut g = vec![vec![]; n];
    for &(u, v) in edges.iter() {
        g[u].push(v);
    }
    UnweightedGraph::new(g)
}
/// 重みなし無向グラフ
pub fn make_undirected_graph(n: usize, edges: &[(usize, usize)]) -> UnweightedGraph {
    let mut g = vec![vec![]; n];
    for &(u, v) in edges.iter() {
        g[u].push(v);
        g[v].push(u);
    }
    UnweightedGraph::new(g)
}
/// 重み付き有向グラフ
pub fn make_weighted_directed_graph<W: Clone>(
    n: usize,
    edges: &[(usize, usize, W)],
) -> WeightedNodeGraph<W> {
    let mut g = vec![vec![]; n];
    for &(u, v, ref w) in edges.iter() {
        g[u].push((v, w.clone()));
    }
    WeightedNodeGraph::new(g)
}
/// 重み付き無向グラフ
pub fn make_weighted_undirected_graph<W: Clone>(
    n: usize,
    edges: &[(usize, usize, W)],
) -> WeightedNodeGraph<W> {
    let mut g = vec![vec![]; n];
    for &(u, v, ref w) in edges.iter() {
        g[u].push((v, w.clone()));
        g[v].push((u, w.clone()));
    }
    WeightedNodeGraph::new(g)
}

/// 重みなしグラフにおいて、幅優先探索を用いて任意の頂点から、各頂点への最短距離を求める
/// dist[i] = (startからiまでの最短距離)
pub fn dist_table<'a, G>(g: &'a G, start: G::NodeId) -> Vec<usize>
where
    G: Graph<'a, NodeId = usize>,
{
    let mut dist = vec![std::usize::MAX; g.len()];
    dist[start] = 0;
    for (f, t) in bfs(g, start) {
        dist[t] = dist[f] + 1;
    }
    dist
}
/// 重みなしグラフでの2頂点の最短距離を幅優先探索で求める
/// goalまでの経路が見つかった時点で探索を打ち切るので、
/// グラフのサイズの大きさに直接の影響を受けないことが期待される
pub fn shortest_path<'a, G: Graph<'a, NodeId = usize>>(
    g: &'a G,
    start: usize,
    goal: usize,
) -> usize {
    let mut dist = vec![std::usize::MAX; g.len()];
    dist[start] = 0;
    for (f, t) in bfs(g, start) {
        if t == goal {
            return dist[f] + 1;
        }
        dist[t] = dist[f] + 1;
    }
    std::usize::MAX
}

// #[fastout]
fn main() {
    input! {
        n: usize, m: usize,
        e: [(Usize1, Usize1); m],
        start: Usize1, goal: Usize1
    }

    let mut edge3 = Vec::new();
    for (i, j) in e {
        edge3.push((n * 0 + i, n * 1 + j));
        edge3.push((n * 1 + i, n * 2 + j));
        edge3.push((n * 2 + i, n * 0 + j));
    }

    let g = make_directed_graph(n * 3, &edge3);
    let ans = shortest_path(&g, start, goal);

    vis!(if ans >= INF { -1 } else { ans as i64 / 3 });
}