import std.conv, std.functional, std.range, std.stdio, std.string; import std.algorithm, std.array, std.bigint, std.bitmanip, std.complex, std.container, std.math, std.mathspecial, std.numeric, std.regex, std.typecons; import core.bitop; class EOFException : Throwable { this() { super("EOF"); } } string[] tokens; string readToken() { for (; tokens.empty; ) { if (stdin.eof) { throw new EOFException; } tokens = readln.split; } auto token = tokens.front; tokens.popFront; return token; } int readInt() { return readToken.to!int; } long readLong() { return readToken.to!long; } real readReal() { return readToken.to!real; } bool chmin(T)(ref T t, in T f) { if (t > f) { t = f; return true; } else { return false; } } bool chmax(T)(ref T t, in T f) { if (t < f) { t = f; return true; } else { return false; } } int binarySearch(alias pred, T)(in T[] as) { int lo = -1, hi = cast(int)(as.length); for (; lo + 1 < hi; ) { const mid = (lo + hi) >> 1; (unaryFun!pred(as[mid]) ? hi : lo) = mid; } return hi; } int lowerBound(T)(in T[] as, T val) { return as.binarySearch!(a => (a >= val)); } int upperBound(T)(in T[] as, T val) { return as.binarySearch!(a => (a > val)); } struct ModInt(int M_) { import std.conv : to; alias M = M_; int x; this(ModInt a) { x = a.x; } this(long a) { x = cast(int)(a % M); if (x < 0) x += M; } ref ModInt opAssign(long a) { return (this = ModInt(a)); } ref ModInt opOpAssign(string op)(ModInt a) { static if (op == "+") { x += a.x; if (x >= M) x -= M; } else static if (op == "-") { x -= a.x; if (x < 0) x += M; } else static if (op == "*") { x = cast(int)((cast(long)(x) * a.x) % M); } else static if (op == "/") { this *= a.inv(); } else static assert(false); return this; } ref ModInt opOpAssign(string op)(long a) { static if (op == "^^") { if (a < 0) return (this = inv()^^(-a)); ModInt t2 = this, te = ModInt(1); for (long e = a; e > 0; e >>= 1) { if (e & 1) te *= t2; t2 *= t2; } x = cast(int)(te.x); return this; } else return mixin("this " ~ op ~ "= ModInt(a)"); } ModInt inv() const { int a = x, b = M, y = 1, z = 0, t; for (; ; ) { t = a / b; a -= t * b; if (a == 0) { assert(b == 1 || b == -1); return ModInt(b * z); } y -= t * z; t = b / a; b -= t * a; if (b == 0) { assert(a == 1 || a == -1); return ModInt(a * y); } z -= t * y; } } ModInt opUnary(string op: "-")() const { return ModInt(-x); } ModInt opBinary(string op, T)(T a) const { return mixin("ModInt(this) " ~ op ~ "= a"); } ModInt opBinaryRight(string op)(long a) const { return mixin("ModInt(a) " ~ op ~ "= this"); } bool opCast(T: bool)() const { return (x != 0); } string toString() const { return x.to!string; } } enum MO = 1000000007; alias Mint = ModInt!MO; // inv[i] = 1 / i, fac[i] = i!, invFac[i] = 1 / i! Mint[] inv, fac, invFac; void prepareFac(int lim) { inv = new Mint[lim]; fac = new Mint[lim]; invFac = new Mint[lim]; inv[1] = 1; foreach (i; 2 .. lim) { inv[i] = -(Mint.M / i) * inv[cast(size_t)(Mint.M % i)]; } fac[0] = invFac[0] = 1; foreach (i; 1 .. lim) { fac[i] = fac[i - 1] * i; invFac[i] = invFac[i - 1] * inv[i]; } } // Find f(n) for the polynomial f s.t. fs = [f(0), f(1), ...] and deg f < |fs| Mint interpolateIota(const(Mint[]) fs, long n) { const fsLen = cast(int)(fs.length); auto prodR = new Mint[fsLen + 1]; prodR[fsLen] = 1; foreach_reverse (i; 0 .. fsLen) prodR[i] = (n - i) * prodR[i + 1]; Mint ans; Mint prodL = 1; for (int i = 0; i < fsLen; ++i) { // (i - 0) ... (i - (i - 1)) (i - (i + 1)) ... (i - (fsLen - 1)) ans += invFac[i] * (((fsLen - 1 - i) & 1) ? -1 : +1) * invFac[fsLen - 1 - i] * fs[i] * prodL * prodR[i + 1]; prodL *= (n - i); } return ans; } // pws[i] = i^d (0 <= i < n) Mint[] getMonomials(long d, int n) { auto lpf = new int[n]; foreach (i; 0 .. n) lpf[i] = i; foreach (p; 2 .. n) if (lpf[p] == p) { for (int i = 2 * p; i < n; i += p) if (lpf[i] > p) lpf[i] = p; } auto pws = new Mint[n]; foreach (i; 0 .. n) { pws[i] = (lpf[i] == i) ? Mint(i)^^d : (pws[lpf[i]] * pws[i / lpf[i]]); } return pws; } // \sum_{i=0}^{\infty} r^i f(i) (deg f <= d) Mint sumPowerPolyLimit(Mint r, int d, const(Mint[]) fs) { assert(r.x != 1, "sumPowerPolyLimit: r != 1 must hold"); assert(d + 1 < inv.length, "sumPowerPolyLimit: inv[d + 1] must be prepared"); assert(d + 1 <= fs.length, "sumPowerPolyLimit: d + 1 < |fs| must hold"); auto rr = new Mint[d + 1]; rr[0] = 1; foreach (i; 1 .. d + 1) rr[i] = rr[i - 1] * r; Mint ans, sumRF; foreach (i; 0 .. d + 1) { sumRF += rr[i] * fs[i]; ans += invFac[d - i] * invFac[i + 1] * (((d - i) & 1) ? -1 : +1) * rr[d - i] * sumRF; } ans *= (1 - r)^^(-(d + 1)) * fac[d + 1]; return ans; } // \sum_{i=0}^{\infty} r^i i^d Mint sumPowerPolyLimit(Mint r, int d) { return sumPowerPolyLimit(r, d, getMonomials(d, d + 1)); } // \sum_{i=0}^{n-1} r^i f(i) (deg f <= d) Mint sumPowerPoly(Mint r, int d, const(Mint[]) fs, long n) { assert(d + 1 < inv.length, "sumPowerPoly: inv[d + 1] must be prepared"); assert(d + 1 <= fs.length, "sumPowerPoly: d + 1 < |fs| must hold"); assert(n >= 0, "sumPowerPoly: n >= 0 must hold."); if (r.x == 0) return (0 < n) ? fs[0] : Mint(0); auto gs = new Mint[d + 2]; Mint rr = 1; gs[0] = 0; foreach (i; 0 .. d + 1) { gs[i + 1] = gs[i] + rr * fs[i]; rr *= r; } if (r.x == 1) return interpolateIota(gs, n); const c = sumPowerPolyLimit(r, d, fs); const rInv = r.inv(); Mint rrInv = 1; foreach (i; 0 .. d + 2) { gs[i] = rrInv * (gs[i] - c); rrInv *= rInv; } return c + r^^n * interpolateIota(gs, n); } // \sum_{i=0}^{n-1} r^i i^d Mint sumPowerPoly(Mint r, int d, long n) { return sumPowerPoly(r, d, getMonomials(d, d + 1), n); } enum LIM = 205; // (n - k + 1) (n + 1)^(k-1) void main() { prepareFac(LIM); auto small = new Mint[LIM]; foreach (n; 0 .. LIM) { small[n] = Mint(n + 1)^^(n - 1) * invFac[n]; } try { for (; ; ) { const N = readLong(); const M = readInt(); const S = readToken(); int[] ones; if (S[0] == 'o') { ones ~= 0; } for (int i = 0, j; i < M; i = j) { for (j = i; j < M && S[i] == S[j]; ++j) {} if (S[i] == '-') { ones ~= (j - i); } } if (S[M - 1] == 'o') { ones ~= 0; } const onesLen = cast(int)(ones.length); debug { writeln("ones = ", ones); } int innerSum; Mint innerNum = 1; alias Query = Tuple!(int[], "as", int, "coef"); auto qss = new Query[][M + 1]; if (onesLen == 1) { qss[0] ~= Query([0], 1); qss[1] ~= Query([0], -M); foreach (m; 2 .. M + 1) { qss[m] ~= Query([m - 2], M - m + 1); } } else { foreach (a; ones[1 .. onesLen - 1]) { innerSum += a; innerNum *= small[a]; } foreach (a; 0 .. ones[0] + 1) foreach (b; 0 .. ones[onesLen - 1] + 1) { qss[M - (ones[0] - a) - (ones[onesLen - 1] - b)] ~= Query([max(a - 1, 0), max(b - 1, 0)], ((a == 0) ? +1 : -1) * ((b == 0) ? +1 : -1)); } } debug { foreach (m; 0 .. M + 1) { writefln("qss[%s] = %s", m, qss[m]); } void pie(string s, int sig) { if (s.length >= 1 && s[0] == '-') { pie(s[1 .. $], sig); pie('o' ~ s[1 .. $], -sig); } else if (s.length >= 1 && s[$ - 1] == '-') { pie(s[0 .. $ - 1], sig); pie(s[0 .. $ - 1] ~ 'o', -sig); } else { // writeln("query ", s.length, " ", s, " ", sig); } } pie(S, +1); } Mint ans; // m = 0 if (onesLen == 1) { ans += Mint(N)^^N * Mint(N + 1); } foreach (m; 1 .. M + 1) { auto nums = new Mint[m + 1]; foreach (ref q; qss[m]) { int sum = innerSum; Mint num = innerNum; foreach (a; q.as) { sum += a; num *= small[a]; } nums[sum] += q.coef * num; } debug { writefln("m = %s, nums = %s", m, nums); } /* 0 <= k <= N - m (N - m - k + 1) (N - m + 1)^(k-1) / k! * (k + l)! * N^(N - (k + l)) = (N^(N - l) / (N - m + 1)) * (N - m - k + 1) ((k + l)! / k!) * ((N - m + 1) / N)^k */ foreach (l; 0 .. m + 1) { if (nums[l]) { auto fs = new Mint[l + 2]; foreach (k; 0 .. l + 2) { fs[k] = (N - m - k + 1) * fac[k + l] * invFac[k]; } const res = sumPowerPoly(Mint(N - m + 1) / Mint(N), l + 1, fs, N - m + 1); ans += nums[l] * Mint(N)^^(N - l) / Mint(N - m + 1) * res; } } } ans *= (N - M + 1); writeln(ans); } } catch (EOFException e) { } }