Buggy python submission (verdict: Time Limit Exceeded):
S = input()
N = len(S)

from collections import defaultdict

reddic = defaultdict(int)
bludic = defaultdict(int)

redarray = [0] * (N+1)
bluarray = [0] * (N+1)

for e,c in enumerate(S):

    if c == "2":
        bludic[e+1] += 2
        bluarray[e+1] += 2
    elif c == "1":
        reddic[e+1] += 1
        bludic[e+1] += 1

        redarray[e+1] += 1
        bluarray[e+1] += 1
    elif c == "0":
        reddic[e+1] += 2
        redarray[e+1] += 2

for i in range(1,N+1):
    redarray[i] += redarray[i-1]
    bluarray[i] += bluarray[i-1]

# print("redarray: ", redarray)
# print("bluarray: ", bluarray)

dp = [ [0] * (2*N+1) for _ in range(2*N+1)]
dp[0][0] = 1
MOD = 998244353

for p in range(1, 2*N+1):
    for r in range(0, p):
        idx = p if p <= N else N
        if r < redarray[idx]:
            dp[p][r+1] += dp[p-1][r] % MOD
        if p-1-r < bluarray[idx]:
            dp[p][r] += dp[p-1][r] % MOD
    # print("{:3d}".format(p), dp[p])

print(dp[2*N][redarray[N]]%MOD)

Fixed submission:
# from : https://twitter.com/tempura_cpp/status/1094238517784326144

S = input()
N = len(S)
MOD = 998244353

red = []
blue = []

for i in range(N):
    if S[i] == "0": # two red balls 
        red.append(i)
        red.append(i)
    if S[i] == "1": # red, blue
        red.append(i)
        blue.append(i)
    if S[i] == "2": # two blue 
        blue.append(i)
        blue.append(i)

DP = [ [0]*4020 for _ in range(4020)] # DP[rsize][bsize] : # of patterns for 
                                           # the case red size is i, blue size is j. 
                                                        
DP[0][0] = 1                          # S = "" pattern.

rsize = len(red)
bsize = len(blue)
# print("red: {}, blue: {}".format(red, blue))

# S = 12001021211100201020
# red:  [0, 2, 2, 3, 3, 4, 5, 5, 7, 9, 10, 11, 12, 12, 13, 13, 15, 15, 16, 17, 17, 19, 19], 
# blue: [0, 1, 1, 4, 6, 6, 7, 8, 8, 9, 10, 11, 14, 14, 16, 18, 18]

# sample
# b r r r r r b
# b r r r r r b
# blue : [0,0, 6,6]
# red  : [1,1,2,2,3,3,4,4,5,5]
# it is hard to use blue[2] = 6, you have to use red 1-5
# this constraint is represented with m = i+j


for i in range(rsize+1):
    for j in range(bsize+1):

        DP[i][j] %= MOD

        m = i+j

        if j < bsize and blue[j] <= m:
            DP[i][j+1] += DP[i][j]

        if i < rsize and red[i] <= m:
            DP[i+1][j] += DP[i][j]


print(DP[rsize][bsize] % MOD)

Buggy python submission (verdict: Time Limit Exceeded):
S = input()
n = len(S)
N = n
# --------------------------------------------------------------------------------
from bisect import bisect_left, bisect_right
# getred = lambda c: 2 if c == "0" else 1 if c == "1" else 0
redidx = []
blueidx = []
for e,c in enumerate(S):
    if c == "2":
        blueidx.append(e+1)
        blueidx.append(e+1)
    elif c == "1":
        blueidx.append(e+1)
        redidx.append(e +1)
    elif c == "0":
        redidx.append(e+1)
        redidx.append(e+1)
dp = [[0] * (2*N+1) for _ in range(2*N+1)]
dp[0][0] = 1

# print("redidx: ", redidx)
# print("blueidx: ", blueidx)


MOD = 998244353
for p in range(1,2*N+1): # p$B2sL\$N%?!<%s$G!"(Br$B8D@V$$6L$r;H$C$F$$$k(B
    for r in range(0,p): # r $B$OA0$N%?!<%s$N(B 0<=r<=p, $BA0$N%?!<%s$G(Bp$B;H$C$F$k$N$O$"$j$($J$$(B

        if r < bisect_right(redidx, p):
            dp[p][r+1] += dp[p-1][r]  % MOD

        if p-1-r < bisect_right(blueidx, p):
            dp[p][r] += dp[p-1][r] % MOD

        # print("p: {p}, r: {r} , dp[{p}]: {dp}, r:{r}, bitright redidx,p: {bit} b:{b}: bitright_blueidx,p: {bit2}".format(p=p, r=r, dp=dp[p], bit=bisect_right(redidx,p), bit2=bisect_right(blueidx,p), b=p-1-r))
        # print("\tredidx: {red},\n\tbluidx: {blu}".format(red=redidx, blu=blueidx))

# print(sum(dp[2*N][len(redidx)]))
print(dp[2*N][len(redidx)] % MOD)
        
# print(redidx)
# print(blueidx)
exit(0)


# --------------------------------------------------------------------------------

sumred = [0] * (n+1)
sumblu = [0] * (n+1)
getred = lambda c: 2 if c == "0" else 1 if c == "1" else 0
# getblu = lambda c: 2 - getred(c)

sumred[0] = 0 # getred(S[0])
sumblu[0] = 0 # getred(S[0])
# sumred[1] = getred(S[0])

for i in range(1,n+1): # i $B?ML\$^$G8+$?;~$N;H$($k@V$$6L$N9g7W(B
    sumred[i] = sumred[i-1] + getred(S[i-1])
    sumblu[i] = sumblu[i-1] + 2 - getred(S[i-1])

dp = [ [0] * (2*N+1) for _ in range(2*N+1)] 

dp[0][0] = 0
dp[0][1] = 0
# dp[1][0] = $B$b$7(B1$B?ML\$,@D$$6L$N$_$@$C$?$i(B($B!"(B
# dp[1][0] = 1$B2sL\!"(B1$B?ML\$,@D$$6L$N$_$@$C$?$i"*(B1$BDL$j(B
# dp[1][0] = 1$B2sL\!"(B1$B?ML\$,@D!&@VN>J};}$C$F$?$i"*(B1$BDL$j(B
# dp[1][1] = 1$B2sL\!"(B1$B?ML\$,(B $BF1>e(B  1$BDL$j(B
# dp[1][2] = $B$J$7(B
dp[1][0] = 1
dp[1][1] = 1


for p in range(1,2*N+1):
    # p$B%?!<%sL\$G!"(Bp$B8D$N6L$r;H$C$F$$$k!#(B
    # $B@D$K$h$C$F:GDc?t$K$b@)Ls$r$+$1$kI,MW$,$"$k!#(B
    # $B;H$($k@D$N6L$N:GBg?t$O!"(Bsumblu[p]$B8D(B
    # i$B?ML\!!(B

    if p <= N:
        for r in range(max(0,p-sumblu[p]), min(sumred[p], p)+1): 
            print("p: {}, r: {}".format(p,r))

            if r > 0:
                dp[p][r] += dp[p-1][r-1]
                dp[p][r] += dp[p-1][r]
            else:
                dp[p][r] += dp[p-1][r]

            print("dp[{p}][{r}] += dp[{a}][{b}]:{d} + dp[{a}][{c}]:{e}".format(p=p,r=r, a=p-1, b=r-1, c=r, d=dp[p-1][r-1], 
                e=dp[p-1][r]))

            dp[p][r] += dp[p-1][r-1] + dp[p-1][r]
    else:
        for r in range(max(0,p-sumblu[N]), min(sumred[N], p)+1): 
            print("p: {}, r: {}".format(p,r))

            if r > 0:
                dp[p][r] += dp[p-1][r-1]
                dp[p][r] += dp[p-1][r]
            else:
                dp[p][r] += dp[p-1][r]

            print("dp[{p}][{r}] += dp[{a}][{b}]:{d} + dp[{a}][{c}]:{e}".format(p=p,r=r, a=p-1, b=r-1, c=r, d=dp[p-1][r-1], 
                e=dp[p-1][r]))

            dp[p][r] += dp[p-1][r-1] + dp[p-1][r]

    print("\t",dp[p][r])
print(dp[2*N][r])


Fixed submission:
# from : https://twitter.com/tempura_cpp/status/1094238517784326144

S = input()
N = len(S)
MOD = 998244353

red = []
blue = []

for i in range(N):
    if S[i] == "0": # two red balls 
        red.append(i)
        red.append(i)
    if S[i] == "1": # red, blue
        red.append(i)
        blue.append(i)
    if S[i] == "2": # two blue 
        blue.append(i)
        blue.append(i)

DP = [ [0]*4020 for _ in range(4020)] # DP[rsize][bsize] : # of patterns for 
                                           # the case red size is i, blue size is j. 
                                                        
DP[0][0] = 1                          # S = "" pattern.

rsize = len(red)
bsize = len(blue)
# print("red: {}, blue: {}".format(red, blue))

# S = 12001021211100201020
# red:  [0, 2, 2, 3, 3, 4, 5, 5, 7, 9, 10, 11, 12, 12, 13, 13, 15, 15, 16, 17, 17, 19, 19], 
# blue: [0, 1, 1, 4, 6, 6, 7, 8, 8, 9, 10, 11, 14, 14, 16, 18, 18]

# sample
# b r r r r r b
# b r r r r r b
# blue : [0,0, 6,6]
# red  : [1,1,2,2,3,3,4,4,5,5]
# it is hard to use blue[2] = 6, you have to use red 1-5
# this constraint is represented with m = i+j


for i in range(rsize+1):
    for j in range(bsize+1):

        DP[i][j] %= MOD

        m = i+j

        if j < bsize and blue[j] <= m:
            DP[i][j+1] += DP[i][j]

        if i < rsize and red[i] <= m:
            DP[i+1][j] += DP[i][j]


print(DP[rsize][bsize] % MOD)