神仙题
先考虑对于一种确定的 $01$ 串如何 $check$ 其合法
我们可以开一个栈,从前往后遍历时对栈进行操作:
1、当前位为 $0$ ,直接加入栈中,如果栈顶有连续的 $3$ 个 $0$ ,就将上面两个弹出(合并)
2、当前位为 $1$ ,如果栈顶为 $1$ 且 $1$ 的个数不到 $2$ (超过 $2$ 个 $1$ 剩下的是没有什么意义的,具体原因看后面),就加入;否则如果是 $0$ 的话,就弹出这个 $0$
为什么加入 $1$ 时栈顶为 $0$ 时可以直接弹出栈顶 ?
其实这个操作就相当于把这个 $1$ 与栈顶的 $0$ 合并了,因为一个 $1$ 和一个 $0$ 最后合成什么还是要看第三个数是什么,所以这里可以直接弹出不管
可以发现栈底是连续的一段 $1$ ,栈顶是连续的一段 $0$ ,且个数都不超过 $2$ 个。
手玩一下可以发现,最后是否可行就看栈中 $1$ 的个数是不是大于等于 $0$ 的个数就行了
证明可以自己去推一下(懒得写了,大概就是从奇偶性下手)
然后我们发现这个栈的状态很好描述(就是可以开个两维,第一维是有几个 $1$ ,第二维有几个 $0$ ,由上面的那条栈内数字的性质可以发现这样是可以描述唯一的一种的)
于是直接 $ ext{DP}$ 套 $ ext{DP}$ (算套 $ ext{DP}$ 吗?反正是个 $ ext{DP}$ 就对了)
$code$ :
#include<cstdio> #include<cctype> #include<cstring> #define maxn 303303 #define mod 1000000007 template<class T> inline T read(){ T r=0,f=0; char c; while(!isigit(c=getchar()))f|=(c=='-'); while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar(); return f?-r:r; } int n; char s[maxn]; long long ans,f[maxn][5][5]; int main(){ scanf("%s",s+1); n=strlen(s+1); f[0][0][0]=1; for(int i=1;i<=n;i++) for(int j=0;j<3;j++) for(int k=0;k<3;k++){ if(s[i]!='0'){ if(k<2)(f[i][j][k]+=f[i-1][j][k+1])%=mod; if(j&&!k){ (f[i][j][k]+=f[i-1][j-1][k])%=mod; if(j==2)(f[i][j][k]+=f[i-1][j][k])%=mod; } } if(s[i]!='1'&&k){ (f[i][j][k]+=f[i-1][j][k-1])%=mod; if(k==1)(f[i][j][k]+=f[i-1][j][2])%=mod; } } for(int i=0;i<3;i++) for(int j=0;j<=i;j++) (ans+=f[n][i][j])%=mod; printf("%lld ",ans); return 0; }