首先考虑对于固定的 01 串怎样计算它是否可以通过将三个连续的 \(0\) 或 \(1\) 替换为其中位数得到。我们考虑单调栈,新建一个栈,栈底到栈顶分别是一段连续的 \(1\) 和一段连续的 \(0\),分别表示当前未合并完的字符,我们从左往右扫描,每遇到一个字符可分以下两种情况:
-
如果我们遇到一个 \(0\):
-
如果栈为空,那直接将这个 \(0\) 入栈就好了。
-
如果栈顶 \(0\) 的个数 \(\ge 2\),那么显然我们可以把三个连续的 \(0\) 合并成一个 \(0\),因为不论我们后来加入什么样的数字,这样一一合并下去最终只可能得到一个 \(0\),也就是说原来栈顶的两个 \(0\) 变成了一个 \(0\)。
-
如果栈顶没有 \(0\),否则直接将这个 \(0\) 入栈。
-
-
如果我们遇到一个 \(1\):
-
如果栈为空,那直接将这个 \(1\) 入栈就好了。
-
如果栈顶有 \(0\),并且栈不为空,那么栈最顶上的两个元素一定是一个 \(0\) 一个 \(1\),而显然如果第一个数是 \(0\),第二个数是 \(1\),那第三个数不论是多少,取中位数得到的结果一定还是第三个数,所以我们索性将最开头的 \(01\) 抵消,也就是说栈中 \(0\) 的个数减少了 \(1\),此时栈底到栈顶还是一段连续的 \(1\) 和一段连续的 \(0\)。
-
否则,栈中一定只剩一段连续的 \(1\)。如果这段 \(1\) 的个数 \(\ge 3\),仿照之前的推理过程它可以等效于 \(3\) 个 \(1\),否则我们就直接将 \(1\) 入栈。
-
显然最终栈不为空,并且该 01 串 \(s\) 满足条件的充要条件是最后 \(1\) 的个数大于 \(0\) 的个数,因为如果在合并过程中不存在栈中剩余 \(\ge 3\) 个 \(1\),并且又进来了一个 \(1\) 的情况,那么每次操作栈内数的个数要么 \(+1\) 要么 \(-1\),而 \(n\) 为奇数,故最后栈中剩余数的个数是奇数。如果只剩一个 \(1\) 那就直接满足条件了,如果剩 \(2\) 个 \(1\) 一个 \(0\) 或者 \(3\) 个 \(1\) 把这三个数合并一下即可,如果剩 \(3\) 个 \(1\) 两个 \(0\) 根据抽屉原理一定存在连续的三个数中恰好有 \(2\) 个 \(1\),把这三个数合并得到一个 \(1\),再把剩下三个数合并即可。如果在合并过程中存在栈中剩余 \(\ge 3\) 个 \(1\),并且又进来了一个 \(1\) 的情况,那么最终这三个 \(1\) 一定不会被消掉,也就是说最终 \(1\) 的个数为 \(3\),严格大于 \(0\) 的个数的最大值 \(2\),也符合题意。
接下来考虑原题。不难发现任何时刻单调栈中 \(1\) 的个数 \(\le 3\),\(0\) 的个数 \(\le 2\),也就是说本质不同的单调栈的个数 \(\le 12\)。我们考虑将单调栈放入 DP 状态中,即设 \(dp_{i,x,y}\) 表示考虑到第 \(i\) 个字符,单调栈中还剩 \(x\) 个 \(0\) 和 \(y\) 个 \(1\) 的方案数。分该位填 \(0\) 和该位填 \(1\) 转移即可。复杂度线性。
const int MAXN=3e5;
const int MOD=1e9+7;
int n,dp[MAXN+5][3][4];char s[MAXN+5];
void add(int &x,int v){((x+=v)>=MOD)&&(x-=MOD);}
int main(){
scanf("%s",s+1);n=strlen(s+1);dp[0][0][0]=1;
for(int i=0;i<n;i++) for(int c0=0;c0<3;c0++) for(int c1=0;c1<4;c1++){
if(s[i+1]!='1'){//digit 0
if(c0==2) add(dp[i+1][1][c1],dp[i][c0][c1]);
else add(dp[i+1][c0+1][c1],dp[i][c0][c1]);
} if(s[i+1]!='0'){//digit 1
if(c0) add(dp[i+1][c0-1][c1],dp[i][c0][c1]);
else add(dp[i+1][c0][min(c1+1,3)],dp[i][c0][c1]);
}
} int ans=0;
for(int c1=1;c1<=3;c1++) for(int c0=0;c0<c1;c0++)
add(ans,dp[n][c0][c1]);
printf("%d\n",ans);
return 0;
}