#include #include #include #include // Using map for sparse DP table using namespace std; // Using long long for intermediate counts potentially, but map value stores int as per MOD arithmetic using ll = long long; // Define the modulus const int MOD = 998244353; // Use map to store DP states. The key is a pair {x, y} representing the state, // and the value is the number of ways to reach this state. // We use two maps to alternate between current and next DP states. map, int> dp_M[2]; int main() { // Faster I/O operations ios_base::sync_with_stdio(false); cin.tie(NULL); string s; // Input string cin >> s; int n = s.length(); // Length of the string // A correct parenthesis sequence must have an even length. // If N is odd, it's impossible to form one, so the answer is 0. if (n % 2 != 0) { cout << 0 << endl; return 0; } ll total_ans = 0; // Initialize total count of good strings // The core idea is based on the necessary and sufficient conditions for a string `t` containing '(', ')', '?' // to be a "good string": // 1. Length N must be even. (Checked above) // Let x_i be the balance of prefix t[1..i] if all '?' are treated as '('. // Let y_i be the balance of prefix t[1..i] if all '?' are treated as ')'. // 2. x_p >= 0 for all prefixes p=1..N. (Balance must not drop below 0 even if '?' are greedily '('. // 3. y_N <= 0. (Final balance must be non-positive if '?' are greedily ')'. Combined with x_N >= 0 (from cond 2), this ensures final balance 0 is achievable) // 4. y_p >= y_N for all prefixes p=1..N. (The balance path treating '?' as ')' must never drop below its final value). // This condition is equivalent to saying the minimum value of the y path occurs at index N. // To count strings satisfying these conditions, we iterate over all possible values M for the final balance y_N. // For a fixed M, we run a DP calculation. The DP counts paths that satisfy: // - x_p >= 0 for all p <= N // - y_p >= M for all p <= N // - y_N = M // The DP state dp[i][x][y] stores the number of ways to form prefix t[1..i] ending in state (x_i, y_i). // We sum the counts for states where y_N = M. Summing over all valid M gives the total answer. // M must be <= 0 (from condition 3). M can range from 0 down to -N. for (int M = 0; M >= -n; --M) { // Initialize DP state for the current value of M int current_M_idx = 0; // Index for current DP table (0 or 1) dp_M[current_M_idx].clear(); // Clear the map for the new M value // Base case: At index 0 (empty prefix), state is (x=0, y=0). // This state is valid only if its y value (0) is >= M. Since M <= 0, this is always true. dp_M[current_M_idx][{0, 0}] = 1; // Dynamic Programming: Iterate through each character of the string s for (int i = 0; i < n; ++i) { int next_M_idx = 1 - current_M_idx; // Index for the next DP table dp_M[next_M_idx].clear(); // Clear the map for the next states // Iterate over all reachable states (x, y) at step i for the current M for (auto const& [state, count] : dp_M[current_M_idx]) { int x = state.first; // Current x value int y = state.second; // Current y value // If count is 0, this state doesn't contribute. Skip. (Map iteration generally handles this). if (count == 0) continue; char c = s[i]; // Character at current index i // Consider transitions based on the character s[i] // Note: '.' can be replaced by '(', ')', or '?' // Possibility 1: The character is '(' or '.' is replaced by '(' if (c == '(' || c == '.') { int next_x = x + 1; // x increases by 1 int next_y = y + 1; // y increases by 1 // Check validity conditions for the next state: x must be non-negative, y must be >= M if (next_x >= 0 && next_y >= M) { // Add the count to the next state, taking modulo dp_M[next_M_idx][{next_x, next_y}] = (dp_M[next_M_idx][{next_x, next_y}] + count) % MOD; } } // Possibility 2: The character is ')' or '.' is replaced by ')' if (c == ')' || c == '.') { int next_x = x - 1; // x decreases by 1 int next_y = y - 1; // y decreases by 1 // Check validity conditions if (next_x >= 0 && next_y >= M) { dp_M[next_M_idx][{next_x, next_y}] = (dp_M[next_M_idx][{next_x, next_y}] + count) % MOD; } } // Possibility 3: The character is '?' or '.' is replaced by '?' if (c == '?' || c == '.') { int next_x = x + 1; // x increases by 1 (treating '?' as '(' for x path) int next_y = y - 1; // y decreases by 1 (treating '?' as ')' for y path) // Check validity conditions if (next_x >= 0 && next_y >= M) { dp_M[next_M_idx][{next_x, next_y}] = (dp_M[next_M_idx][{next_x, next_y}] + count) % MOD; } } } // Switch to the next DP table index for the next character processing current_M_idx = next_M_idx; } // After processing all N characters: // Sum up the counts for final states where y_N = M. // These paths satisfy all conditions for a good string ending with minimum y path value M. for(auto const& [state, count] : dp_M[current_M_idx]) { int y = state.second; // Final y value // Check if the final y coordinate is exactly M if (y == M) { total_ans = (total_ans + count) % MOD; // Add count to the total answer } } } // Output the final total count modulo MOD cout << total_ans << endl; return 0; }