using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
using static System.Math;
class Z { static void Main() => new K(); }
class K
{
    int F => int.Parse(Str);
    long FL => long.Parse(Str);
    int[] G => Strs.Select(int.Parse).ToArray();
    long[] GL => Strs.Select(long.Parse).ToArray();
    string Str => ReadLine();
    string[] Strs => Str.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
    const int MOD = 998244353;
    public K()
    {
        SetOut(new StreamWriter(OpenStandardOutput()) { AutoFlush = false });
        WriteLine(Solve());
        Out.Flush();
    }
    long Solve()
    {
        var S = Str;
        var N = S.Length;
        var d = new Dictionary<P, long> { [new P(0, 0)] = 1 };
        fact = new long[5000];
        ifact = new long[5000]; fact[0] = ifact[0] = 1;
        for (var i = 1; i < 5000; i++) fact[i] = i * fact[i - 1] % MOD;
        for (var i = 1; i < 5000; i++) ifact[i] = Inverse(fact[i], MOD);
        for (var i = 0; i < N; i++)
        {
            int b = S[i] - '0', r = 2 - b;
            var next = new Dictionary<P, long>();
            foreach (var s in d.Keys)
            {
                var t = new P(s.R + r, s.B + b);
                if (t.R > 0)
                {
                    var x = new P(t.R - 1, t.B);
                    if (!next.ContainsKey(x)) next[x] = 0;
                    next[x] += d[s];
                }
                if (t.B > 0)
                {
                    var x = new P(t.R, t.B - 1);
                    if (!next.ContainsKey(x)) next[x] = 0;
                    next[x] += d[s];
                }
            }
            d.Clear();
            foreach (var s in next.Keys) d[s] = next[s] % MOD;
        }
        var ans = 0L;
        foreach (var t in d) ans = (ans + t.Value * Binomial(t.Key.R + t.Key.B, t.Key.R) % MOD) % MOD;
        return ans;
    }
    long[] fact, ifact;
    long Binomial(int n, int m) => fact[n] * ifact[m] % MOD * ifact[n - m] % MOD;
    public static long Inverse(long a, long mod)
    {
        if (a < 0) { a %= mod; if (a < 0) a += mod; }
        var t = SolveLinear(a, mod);
        return t.Item1 > 0 ? t.Item1 : t.Item1 + mod;
    }
    public static Tuple<long, long> SolveLinear(long n, long m)
    {
        if (n < 0) { var p = SolveLinear(-n, m); return p == null ? p : new Tuple<long, long>(-p.Item1, p.Item2); }
        if (m < 0) { var p = SolveLinear(n, -m); return p == null ? p : new Tuple<long, long>(p.Item1, -p.Item2); }
        if (n < m) { var p = SolveLinear(m, n); return p == null ? p : new Tuple<long, long>(p.Item2, p.Item1); }
        long a = 1, b = 0, c = 0, d = 1;
        while (m > 0)
        {
            var r = n % m;
            var q = n / m;
            n = m;
            m = r;
            var tmp = a;
            a = -a * q + b;
            b = tmp;
            tmp = c;
            c = -c * q + d;
            d = tmp;
        }
        return n != 1 ? null : new Tuple<long, long>(d, b);
    }
}
struct P : IEquatable<P>
{
    public readonly int R, B;
    public P(int r, int b) { R = r; B = b; }
    public bool Equals(P other) => R == other.R && B == other.B;
    public override bool Equals(object obj) => Equals((P)obj);
    public override int GetHashCode() => CombineHashCodes(R.GetHashCode(), B.GetHashCode());
    static int CombineHashCodes(int h1, int h2) => ((h1 << 5) + h1) ^ h2;
}

Measured memory_bytes: 36296