use std::collections::HashMap;
use std::io::*;
use std::str::FromStr;

#[allow(dead_code)]
fn read<T: FromStr>() -> T {
    let stdin = stdin();
    let stdin = stdin.lock();
    let token: String = stdin
        .bytes()
        .map(|c| c.expect("failed to read char") as char)
        .skip_while(|c| c.is_whitespace())
        .take_while(|c| !c.is_whitespace())
        .collect();
    token.parse().ok().expect("failed to parse token")
}

#[allow(dead_code)]
struct XorShift {
    x: u32,
    y: u32,
    z: u32,
    w: u32,
}
#[allow(dead_code)]
impl XorShift {
    fn new() -> XorShift {
        XorShift {
            x: 0x34fb2383,
            y: 0x327328fa,
            z: 0xabd4b54a,
            w: 0xa9dba8d1,
        }
    }
    fn seed(s: u32) -> XorShift {
        let mut rnd = XorShift::new();
        rnd.w = s;
        rnd
    }
    fn xor128(&mut self) -> u32 {
        let t = self.x ^ (self.x << 11);
        self.x = self.y;
        self.y = self.z;
        self.z = self.w;
        self.w = (self.w ^ (self.w >> 19)) ^ (t ^ (t >> 8));
        self.w
    }
    fn next_i32(&mut self, r: i32) -> i32 {
        (self.xor128() % r as u32) as i32
    }
    fn next_i32_lr(&mut self, l: i32, r: i32) -> i32 {
        self.next_i32(r - l + 1) + l
    }
    fn next_i64(&mut self, r: i64) -> i64 {
        ((((self.xor128() as u64) << 32) + self.xor128() as u64) % r as u64) as i64
    }
    fn next_i64_lr(&mut self, l: i64, r: i64) -> i64 {
        self.next_i64(r - l + 1) + l
    }
    fn next_f64(&mut self, r: f64) -> f64 {
        (self.xor128() as f64) / (0xffffffffu32 as f64) * r
    }
    fn next_f64_lr(&mut self, l: f64, r: f64) -> f64 {
        self.next_f64(r - l) + l
    }
}

const MOD: i64 = 1e+9 as i64 + 7;
#[allow(dead_code)]
fn pow_mod(base: i64, power: i64, modulo: i64) -> i64 {
    let mut base = base;
    let mut power = power;
    let mut ans = 1;
    while power > 0 {
        if power & 1 == 1 {
            ans = ans * base % modulo;
        }
        power >>= 1;
        base = base * base % modulo;
    }
    ans
}

#[allow(dead_code)]
fn inv_mod(a: i64, modulo: i64) -> i64 {
    pow_mod(a, modulo - 2, modulo)
}

struct Solver {
    n: usize,
    s: Vec<char>,
    xs: Vec<i64>,
    aseq: Vec<Vec<i64>>,
    bseq: Vec<Vec<i64>>,
    ba: Vec<HashMap<i64, usize>>,
}

impl Solver {
    fn new(n: usize, s: Vec<char>) -> Solver {
        let mut rnd = XorShift::new();
        let mut xs = vec![0; 6];
        for i in 0..6 {
            xs[i] = rnd.next_i64(MOD);
        }
        let aseq = vec![vec![0; n + 1]; xs.len()];
        let bseq = vec![vec![0; n + 1]; xs.len()];
        let ba = vec![HashMap::<i64, usize>::new(); xs.len()];
        Solver {
            n: n,
            s: s,
            xs: xs,
            aseq: aseq,
            bseq: bseq,
            ba: ba,
        }
    }
    fn init(&mut self) {
        let n = self.n;
        for (i, &x) in self.xs.iter().enumerate() {
            let inv_x = inv_mod(x, MOD);
            self.aseq[i][n] = 1;
            self.bseq[i][n] = 0;
            for j in (0..n).rev() {
                let c = self.s[j];
                match c {
                    '+' => {
                        self.aseq[i][j] = self.aseq[i][j + 1];
                        self.bseq[i][j] = (self.bseq[i][j + 1] + 1) % MOD;
                    }
                    '-' => {
                        self.aseq[i][j] = self.aseq[i][j + 1];
                        self.bseq[i][j] = (self.bseq[i][j + 1] - 1 + MOD) % MOD;
                    }
                    '>' => {
                        self.aseq[i][j] = (self.aseq[i][j + 1] * x) % MOD;
                        self.bseq[i][j] = (self.bseq[i][j + 1] * x) % MOD;
                    }
                    '<' => {
                        self.aseq[i][j] = (self.aseq[i][j + 1] * inv_x) % MOD;
                        self.bseq[i][j] = (self.bseq[i][j + 1] * inv_x) % MOD;
                    }
                    _ => {
                        assert!(false);
                    }
                }
            }
        }
    }
    fn solve(&mut self) -> usize {
        self.init();
        let mut ans = 0;
        for i in 0..self.xs.len() {
            self.ba[i].insert(0, 1);
        }
        for i in (0..self.n).rev() {
            let mut cnt = self.n;
            for j in 0..self.xs.len() {
                let mut target = (self.bseq[j][i] - self.bseq[j][0] + MOD) % MOD
                    * inv_mod(self.aseq[j][i], MOD) % MOD;
                if let Some(&lcnt) = self.ba[j].get(&target) {
                    cnt = std::cmp::min(cnt, lcnt);
                } else {
                    cnt = 0;
                }
                let v = self.bseq[j][i] * inv_mod(self.aseq[j][i], MOD) % MOD;
                *self.ba[j].entry(v).or_insert(0) += 1;
            }
            // println!("{} {}", i, cnt);
            ans += cnt;
        }
        ans
    }
}

fn main() {
    let n = read::<usize>();
    let s = read::<String>().chars().collect::<Vec<char>>();
    let mut solver = Solver::new(n, s);
    let ans = solver.solve();
    println!("{}", ans);
}


Measured memory_bytes: 115176