题意
两个串,有一些地方已经被填上了-1,1。
现在有两个指针让你操作,每次可以选择一个指针向前移动一步,扫完序列的指针不能再动。
当两个指针走过路径的和(前缀和)小于0时就失败了。

指针的操作方式有一些限制:两个指针下一步中一个是1,另一个是-1时,必须走1的那一个指针。
现在要你把剩下的空位填上-1或1,问无论怎么走都能在不失败的情况下填数的方案。

序列长度5000\leq 5000


先考虑对于一个特定的情况合不合法。
考虑走的过程中的最小值。
实际上达到最小值的情况是走了一步-1:

  • 两个指针后面都是-1
  • 一个指针走到了终点

所以在走最小值前一步时,两个点都会在-1前或终点处。
所以可以只用考虑-1前面的点和终点处 两个序列的最小前缀和 之和。(下文中最小前缀和均代表-1前面的点和终点处的前缀和的最小值)
当这个最小值<1时就意味这他们无法再应对之后到来的-1,所以这里前缀和之和需要>0。
(两个点都在终点处时两个序列都不会再-1了,这时>=0即可,下文不再考虑)

有一个这样的性质:当一个指针在-1之前或结尾时,另一个指针是可以随便跑的。
所以说满足前面指针条件的任何前缀和之和<=0的情况,都可以在一个点到达目标点后直接将另一个点移至对应的最小值,导致任务失败。
所以-1前和终点处的最小前缀和的条件对答案是必要的。

“为什么这样就可行呢?如果两个后面都是-1的话为什么不可能时前缀和从1变成-1?”
考虑最小前缀和之和为1的情况:
考虑走一步。
这时候一个点在-1上,另一个点在-1前,这时前缀和之和为0。
在-1前的点能走到-1的条件是另一行的下一个点也要是-1或者另一行走到了结尾。
无论是结尾处的前缀和还是下一个-1前的前缀和显然都要被统计(这里比原最小前缀和多了个-1),这样最小前缀和就不是1了。
与假设相矛盾。

会发现这时候答案只和两行的最小前缀和有关,于是可以分开来DP。
发现从前往后DP还同时记录历史最小前缀和和结尾的前缀和,十分麻烦,于是考虑从后往前。

用DP[i][j][k(0/1)]表示目前第i位,当前串最小前缀和为j,结尾前缀和与最小前缀和的关系为k的方案数。
(只要前面有和结尾相同的前缀和,最小前缀和就不应该与结尾有关了)
这时候往前新加一个数会发现最小前缀和要么加上增量,要么变成-1(新加的数)。
两边分别DP出来后前缀和优化卷积,对符合条件的求和就行。
在实际DP时并不需要考虑后一项是不是-1的问题,只需要判断开始处的0是否需要被考虑。

code:

#include <iostream>
#include <cstdio>
#include <cstring>
typedef long long lint;
const int N=5005;
const int mod=998244353;

template <class T> inline void twk(T &x){
	if(x>=mod) x-=mod;
}

inline int nxi(){
	int x=0;
	char c;
	while((c=getchar())>'9'||c<'0');
	while(x=x*10-48+c,(c=getchar())>='0'&&c<='9');
	return x;
}

inline void get(int dp[][2]){
	static int tdp[N<<1][2];
	static char ch[N];
	scanf("%s",ch+1);
	int n=strlen(ch+1);
	dp[N][1]=1;
	for(int i=n; i; --i){
		const int dis=n-i+1;
		memcpy(tdp+N-dis,dp+N-dis,((dis+1)<<1)*sizeof(tdp[0]));
		memset(dp+N-dis,0,((dis+1)<<1)*sizeof(tdp[0]));
		for(int j=N-dis; j<=N+dis; ++j){
			if(ch[i]!='V'){
				if(j<=N){
					twk(dp[j-1][0]+=tdp[j][0]);
					twk(dp[j-1][1]+=tdp[j][1]);
				}
				else{
					twk(dp[N][0]+=tdp[j][0]);
					twk(dp[N][0]+=tdp[j][1]);
				}
			}
			if(ch[i]!='P'){
				twk(dp[j+1][0]+=tdp[j][0]);
				twk(dp[j+1][1]+=tdp[j][1]);
			}
		}
	}
}

inline int solve(int dp0[][2],int dp1[][2]){
	int ans=0;
	for(int i=1; i<N<<1; ++i){
		ans=(ans+(lint)dp0[i][1]*dp1[(N<<1)-i][1])%mod;
	}
	for(int i=(N<<1)-1; i; --i){
		twk(dp1[i-1][0]+=dp1[i][0]);
		twk(dp1[i-1][1]+=dp1[i][1]);
	}
	for(int i=1; i<N<<1; ++i){
		ans=(ans+(lint)(dp0[i][0]+dp0[i][1])*dp1[(N<<1)-i+1][0])%mod;
		ans=(ans+(lint)(dp0[i][0]+dp0[i][1])*dp1[(N<<1)-i+1][1])%mod;
	}
	return ans;
}

int main(){
	static int dp0[N<<1][2],dp1[N<<1][2];
	get(dp0);
	get(dp1);
	printf("%d\n",solve(dp0,dp1));
	return 0;
}