AtCoder Beginner Contest 398 D

https://atcoder.jp/contests/abc398/tasks/abc398_d

風で煙を動かすのではなく、原点とRCを風と反対方向に動かします。

// Bonfire
#![allow(non_snake_case)]

use std::collections::HashSet;


//////////////////// library ////////////////////

fn read<T: std::str::FromStr>() -> T {
    let mut line = String::new();
    std::io::stdin().read_line(&mut line).ok();
    line.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    read::<String>().split_whitespace()
            .map(|e| e.parse().ok().unwrap()).collect()
}


//////////////////// Point ////////////////////

#[derive(Clone, Copy, Hash, Eq, PartialEq)]
struct Point {
    x: i32,
    y: i32
}

use std::ops::Sub;

impl Sub for Point {
    type Output = Self;
    
    fn sub(self, other: Self) -> Self {
        let x = self.x - other.x;
        let y = self.y - other.y;
        Point { x, y }
    }
}

impl Point {
    fn new(x: i32, y: i32) -> Point {
        Point { x, y }
    }
}


//////////////////// process ////////////////////

fn read_input() -> (Point, String) {
    let v: Vec<i32> = read_vec();
    let RC = Point::new(v[1], v[2]);
    let S: String = read();
    (RC, S)
}

fn dir(c: char) -> Point {
    match c {
        'N' => Point::new(-1, 0),
        'W' => Point::new(0, -1),
        'S' => Point::new(1, 0),
        _   => Point::new(0, 1)
    }
}

fn F(RC: Point, S: String) -> String {
    let mut P0 = Point::new(0, 0);
    let mut P1 = RC;
    let mut bs: Vec<char> = vec![];
    let mut smokes: HashSet<Point> = HashSet::new();
    smokes.insert(P0);
    for c in S.chars() {
        let d = dir(c);
        P0 = P0 - d;
        P1 = P1 - d;
        smokes.insert(P0);
        bs.push(if smokes.contains(&P1) { '1' } else { '0' })
    }
    bs.into_iter().collect::<String>()
}

fn main() {
    let (RC, S) = read_input();
    println!("{}", F(RC, S))
}