Problem:
Score : 1200 points

Problem Statement

In Takahashi's mind, there is always an integer sequence of length 2 \times 10^9 + 1: A = (A_{-10^9}, A_{-10^9 + 1}, ..., A_{10^9 - 1}, A_{10^9}) and an integer P.

Initially, all the elements in the sequence A in Takahashi's mind are 0, and the value of the integer P is 0.

When Takahashi eats symbols +, -, > and <, the sequence A and the integer P will change as follows:

When he eats +, the value of A_P increases by 1;

When he eats -, the value of A_P decreases by 1;

When he eats >, the value of P increases by 1;

When he eats <, the value of P decreases by 1.

Takahashi has a string S of length N. Each character in S is one of the symbols +, -, > and <.
He chose a pair of integers (i, j) such that 1 \leq i \leq j \leq N and ate the symbols that are the i-th, (i+1)-th, ..., j-th characters in S, in this order.
We heard that, after he finished eating, the sequence A became the same as if he had eaten all the symbols in S from first to last.
How many such possible pairs (i, j) are there?

Constraints

1 \leq N \leq 250000

|S| = N

Each character in S is +, -, > or <.

Input

Input is given from Standard Input in the following format:

N
S

Output

Print the answer.

Sample Input 1

5
+>+<-

Sample Output 1

3

If Takahashi eats all the symbols in S, A_1 = 1 and all other elements would be 0.
The pairs (i, j) that leads to the same sequence A are as follows:

(1, 5)

(2, 3)

(2, 4)

Sample Input 2

5
+>+-<

Sample Output 2

5

Note that the value of P may be different from the value when Takahashi eats all the symbols in S.

Sample Input 3

48
-+><<><><><>>>+-<<>->>><<><<-+<>><+<<>+><-+->><<

Sample Output 3

475

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

// use std::cmp;
// use std::collections::HashMap;
// use std::collections::HashSet;
// use std::collections::VecDeque;
use std::collections::BTreeMap;


// const INF: i64 = 1_000_000_000;

const MOD: i64 = 1_000_000_009;


fn invmod(a: i64, p: i64) -> i64 {
    if a == 1 {
        return 1;
    } else {
        return (1 - p*invmod(p%a, a)) / a + p;
    }
}


fn eat(ch: char, tape: &mut Vec<i64>, pos: &mut Vec<i64>, X: &Vec<i64>, Xinv: &Vec<i64>) {
    match ch {
        '+' => {
            for i in 0..4 {
                tape[i] += pos[i];
                tape[i] %= MOD;
            }
        }
        '-' => {
            for i in 0..4 {
                tape[i] -= pos[i];
                tape[i] += MOD;
                tape[i] %= MOD;
            }
        }
        '>' => {
            for i in 0..4 {
                pos[i] *= X[i];
                pos[i] %= MOD;
            }
        }
        '<' => {
            for i in 0..4 {
                pos[i] *= Xinv[i];
                pos[i] %= MOD;
            }
        }
        _ => panic!()
    }
}



fn main() {
    let cin = stdin();
    let cin = cin.lock();
    let mut sc = Scanner::new(cin);

    let X: Vec<i64> = vec![31, 41, 592, 653];
    let mut Xinv: Vec<i64> = vec![];
    for i in 0..4 {
        Xinv.push(invmod(X[i], MOD));
    }
    let Xinv = Xinv;
    //println!("{:?}, {:?}", X, Xinv);

    let n: usize = sc.read();
    let s: String = sc.read();
    let s = s.chars().collect::<Vec<_>>();
    //println!("{}, {:?}", n, s);
    let mut tape = vec![0, 0, 0, 0];
    let mut pos = vec![1, 1, 1, 1];
    for &ch in s.iter() {
        eat(ch, &mut tape, &mut pos, &X, &Xinv);
    }
    let target = tape;
    //println!("{:?}", target);
    let mut goals = BTreeMap::new();
    let mut tape = vec![0, 0, 0, 0];
    let mut pos = vec![1, 1, 1, 1];
    let mut ans: i64 = 0;
    for &ch in s.iter() {
        {
            let mut goal: Vec<i64> = vec![];
            for i in 0..4 {
                goal.push((tape[i] + pos[i] * target[i]) % MOD);
            }
            let cnt = goals.remove(&goal).unwrap_or(0);
            goals.insert(goal, cnt+1);
        }
        eat(ch, &mut tape, &mut pos, &X, &Xinv);
        ans += *(goals.get(&tape).unwrap_or(&0));
        //println!("{:?}", goals);
    }
    println!("{}", ans);
}

// thanks, ir5
use std::io::{stdin, Read, StdinLock};
use std::str::FromStr;

struct Scanner<'a> {
    cin: StdinLock<'a>,
}

impl<'a> Scanner<'a> {
    fn new(cin: StdinLock<'a>) -> Scanner<'a> {
        Scanner { cin: cin }
    }

    fn read1<T: FromStr>(&mut self) -> Option<T> {
        let token = self.cin
            .by_ref()
            .bytes()
            .map(|c| c.unwrap() as char)
            .skip_while(|c| c.is_whitespace())
            .take_while(|c| !c.is_whitespace())
            .collect::<String>();
        token.parse::<T>().ok()
    }

    fn read<T: FromStr>(&mut self) -> T {
        self.read1().unwrap()
    }
}