#include #define rep(i, j, k) for (int i = (j); i <= (k); ++i) #define per(i, j, k) for (int i = (j); i >= (k); --i) #define SZ(v) int((v).size()) #define ALL(v) (v).begin(),(v).end() #define fi first #define se second using ll = long long; using pii = std::pair; using pll = std::pair; template void chkmn(T &x, T y) { if (y < x) x = y; } template void chkmx(T &x, T y) { if (y > x) x = y; } using namespace std; template class mod_int { using Z = mod_int; private: static int mo(int x) { return x < 0 ? x + P : x; } public: int x; int val() const { return x; } mod_int() : x(0) {} template mod_int(const T &x_) : x(x_ >= 0 && x_ < P ? static_cast(x_) : mo(static_cast(x_ % P))) {} bool operator==(const Z &rhs) const { return x == rhs.x; } bool operator!=(const Z &rhs) const { return x != rhs.x; } Z operator-() const { return Z(x ? P - x : 0); } Z pow(long long k) const { Z res = 1, t = *this; while (k) { if (k & 1) res *= t; if (k >>= 1) t *= t; } return res; } Z &operator++() { x < P - 1 ? ++x : x = 0; return *this; } Z &operator--() { x ? --x : x = P - 1; return *this; } Z operator++(int) { Z ret = x; x < P - 1 ? ++x : x = 0; return ret; } Z operator--(int) { Z ret = x; x ? --x : x = P - 1; return ret; } Z inv() const { return pow(P - 2); } Z &operator+=(const Z &rhs) { (x += rhs.x) >= P && (x -= P); return *this; } Z &operator-=(const Z &rhs) { (x -= rhs.x) < 0 && (x += P); return *this; } Z &operator*=(const Z &rhs) { x = 1ULL * x * rhs.x % P; return *this; } Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); } #define setO(T, o) \ friend T operator o(const Z &lhs, const Z &rhs) {\ Z res = lhs; \ return res o## = rhs; \ } setO(Z, +) setO(Z, -) setO(Z, *) setO(Z, /) #undef setO }; const int P = 998244353; using Z = mod_int

; namespace Poly_space { std::vector rev; std::vector roots{0, 1}; void dft(std::vector &a) { int n = a.size(); if (int(rev.size()) != n) { int k = __builtin_ctz(n) - 1; rev.resize(n); for (int i = 0; i < n; i++) { rev[i] = rev[i >> 1] >> 1 | (i & 1 ? 1 << k : 0); } } for (int i = 0; i < n; i++) if (rev[i] < i) std::swap(a[i], a[rev[i]]); if (int(roots.size()) < n) { int k = __builtin_ctz(roots.size()); roots.resize(n); while ((1 << k) < n) { Z e = Z(3).pow((P - 1) >> (k + 1)); for (int i = 1 << (k - 1); i < (1 << k); i++) roots[2 * i] = roots[i], roots[2 * i + 1] = roots[i] * e; k++; } } for (int k = 1; k < n; k *= 2) for (int i = 0; i < n; i += 2 * k) for (int j = 0; j < k; j++) { Z u = a[i + j], v = a[i + j + k] * roots[k + j]; a[i + j] = u + v, a[i + j + k] = u - v; } } void idft(std::vector &a) { int n = a.size(); std::reverse(a.begin() + 1, a.end()); dft(a); Z inv = (1 - P) / n; for (int i = 0; i < n; i++) a[i] *= inv; } struct Poly { std::vector a; Poly() {} Poly(const std::vector &a) : a(a) {} Poly(const std::initializer_list &a) : a(a) {} int size() const { return a.size(); } void resize(int n) { a.resize(n); } Z operator[](int idx) const { if (idx < 0 || idx >= size()) return 0; return a[idx]; } Z &operator[](int idx) { return a[idx]; } Poly mulxk(int k) const { auto b = a; b.insert(b.begin(), k, 0); return Poly(b); } Poly modxk(int k) const { k = std::min(k, size()); return Poly(std::vector(a.begin(), a.begin() + k)); } Poly divxk(int k) const { if (size() <= k) return Poly(); return Poly(std::vector(a.begin() + k, a.end())); } friend Poly operator+(const Poly &a, const Poly &b) { std::vector res(std::max(a.size(), b.size())); for (int i = 0; i < int(res.size()); i++) res[i] = a[i] + b[i]; return Poly(res); } friend Poly operator-(const Poly &a, const Poly &b) { std::vector res(std::max(a.size(), b.size())); for (int i = 0; i < int(res.size()); i++) res[i] = a[i] - b[i]; return Poly(res); } friend Poly operator*(Poly a, Poly b) { if (a.size() == 0 || b.size() == 0) return Poly(); int sz = 1, tot = a.size() + b.size() - 1; while (sz < tot) sz *= 2; a.a.resize(sz), b.a.resize(sz), dft(a.a), dft(b.a); for (int i = 0; i < sz; ++i) a.a[i] = a[i] * b[i]; idft(a.a), a.resize(tot); return a; } friend Poly operator*(Z a, Poly b) { for (int i = 0; i < int(b.size()); i++) b[i] *= a; return b; } friend Poly operator*(Poly a, Z b) { for (int i = 0; i < int(a.size()); i++) a[i] *= b; return a; } Poly &operator+=(Poly b) { return (*this) = (*this) + b; } Poly &operator-=(Poly b) { return (*this) = (*this) - b; } Poly &operator*=(Poly b) { return (*this) = (*this) * b; } Poly deriv() const { if (a.empty()) return Poly(); std::vector res(size() - 1); for (int i = 0; i < size() - 1; ++i) res[i] = (i + 1) * a[i + 1]; return Poly(res); } Poly integr() const { std::vector res(size() + 1); for (int i = 0; i < size(); ++i) res[i + 1] = a[i] / (i + 1); return Poly(res); } Poly inv(int m) const { Poly x{a[0].inv()}; int k = 1; while (k < m) k *= 2, x = (x * (Poly{2} - modxk(k) * x)).modxk(k); return x.modxk(m); } Poly log(int m) const { return (deriv() * inv(m)).integr().modxk(m); } Poly exp(int m) const { Poly x{1}; int k = 1; while (k < m) k *= 2, x = (x * (Poly{1} - x.log(k) + modxk(k))).modxk(k); return x.modxk(m); } Poly sqrt(int m) const { Poly x{1}; int k = 1; while (k < m) k *= 2, x = (x + (modxk(k) * x.inv(k)).modxk(k)) * ((P + 1) / 2); return x.modxk(m); } Poly mulT(Poly b) const { if (b.size() == 0) return Poly(); int n = b.size(); std::reverse(b.a.begin(), b.a.end()); return ((*this) * b).divxk(n - 1); } std::vector eval(std::vector x) const { if (size() == 0) return std::vector(x.size(), 0); const int n = std::max(int(x.size()), size()); std::vector q(4 * n); std::vector ans(x.size()); x.resize(n); std::function build = [&](int p, int l, int r) { if (r - l == 1) q[p] = Poly{1, -x[l]}; else { int m = (l + r) / 2; build(2 * p, l, m), build(2 * p + 1, m, r); q[p] = q[2 * p] * q[2 * p + 1]; } }; build(1, 0, n); std::function work = [&](int p, int l, int r, const Poly &num) { if (r - l == 1) { if (l < int(ans.size())) ans[l] = num[0]; } else { int m = (l + r) / 2; work(2 * p, l, m, num.mulT(q[2 * p + 1]).modxk(m - l)); work(2 * p + 1, m, r, num.mulT(q[2 * p]).modxk(r - m)); } }; work(1, 0, n, mulT(q[1].inv(n))); return ans; } }; } using namespace Poly_space; const int maxn = 400010; int n, a[maxn], tot, cnt; Z fac[maxn], ivf[maxn]; Poly f[maxn]; string str; Z binom(int x, int y) { return fac[x] * ivf[y] * ivf[x - y]; } void ins(int len) { int cur = 0; f[++cnt].a.emplace_back(1); while (true) { cur++; if (2 * cur - 1 > len) break; f[cnt].a.emplace_back(binom(len - cur + 1, cur)); } } Poly solve(int l, int r) { if (l == r) return f[l]; int mid = (l + r) >> 1; return solve(l, mid) * solve(mid + 1, r); } Z cat(int x) { return binom(2 * x, x) / (x + 1); } int main() { fac[0] = 1; rep (i, 1, maxn - 1) fac[i] = fac[i - 1] * i; ivf[maxn - 1] = fac[maxn - 1].inv(); per (i, maxn - 1, 1) ivf[i - 1] = ivf[i] * i; cin.tie(nullptr) -> ios::sync_with_stdio(false); cin >> n >> str, n--; rep (i, 1, n) if (str[i - 1] == '<') a[++tot] = i; if (!tot) return cout << "0\n", 0; rep (i, 1, tot - 1) if (a[i] + 1 == a[i + 1]) return cout << "0\n", 0; if (a[1] - 2 > 0) ins(a[1] - 2); if (n - a[tot] - 1 > 0) ins(n - a[tot] - 1); rep (i, 1, tot - 1) if (a[i + 1] - a[i] - 3 > 0) ins(a[i + 1] - a[i] - 3); f[++cnt].a.emplace_back(1); auto F = solve(1, cnt); Z ans = 0; rep (i, 0, SZ(F) - 1) ans += Z((i & 1) ? P - 1 : 1) * F[i] * cat(n + 1 - tot - i); cout << ans.val() << '\n'; }