正题
首先考虑到3个0的肯定可以先删掉,那么两个1之间最多只有两个0。
把一个序列写成0,1,2三个数字组成的形式,分别表示在第i个1和第i-1个1之间0的个数,首尾情况考虑先插一个1.
然后发现单独的0的情况其实是可以直接删除的,除非只剩下一个0,因为附近肯定有一个1,所以,01不论与1还是与0结合,都会生成一个与结合数字相同的数字,去掉它对正确性并没有影响,因为接下来的操作对于每一个0块独立。
那么现在只剩下一个只存在02的长度为偶数的数列,那么我们需要经过一波操作使得剩下的数列为00.(代表中间有一个1)
找规律可以发现,两个0可以与周围的一个1结合生成一个0,而这个0,会与相邻的0个0或者2个0,生成1个0,也就是说去掉一个2,要使相邻的其中一个变为1,然后变为1了,就可以直接删除了,所以对于这样的数列来说,一删就是删两个。
可以证明,只有当分别存在一个奇数位上的0和一个偶数位上的0时,才会剩下00,具体怎么证明可以暴力枚举其他的情况。
那么就可以构造一个Dp,完结撒花。
另外一种很神奇的做法就是毕克自动机,我也不知道为什么是对的,也不知道怎么构造的,详见其他的Blog。
#include<bits/stdc++.h>
using namespace std;
long long f[2][3][2][3];//状态,位置,0个数
const long long mod=1e9+7;
int n;
char s[1000010];
int main(){
int op=0;
f[0][0][1][0]=1;
scanf("%s",s+1);
n=strlen(s+1);s[n+1]='1';
for(int i=1;i<=n+1;i++){
op^=1;memset(f[op],0,sizeof(f[op]));
if(s[i]!='0'){
f[op][0][1][0]+=f[op^1][0][0][0]+f[op^1][0][0][2]+f[op^1][0][1][1];
f[op][0][0][0]+=f[op^1][0][0][1]+f[op^1][0][1][2];
f[op][1][1][0]+=f[op^1][1][0][2]+f[op^1][1][1][1];
f[op][1][0][0]+=f[op^1][0][1][0]+f[op^1][1][1][0]+f[op^1][1][1][2]+f[op^1][1][0][1];
f[op][2][1][0]+=f[op^1][1][0][0]+f[op^1][2][0][2]+f[op^1][2][1][1]+f[op^1][2][0][0];
f[op][2][0][0]+=f[op^1][2][1][2]+f[op^1][2][1][0]+f[op^1][2][0][1];
}
if(s[i]!='1'){
for(int j=0;j<3;j++)
for(int k=0;k<2;k++)
f[op][j][k][2]+=f[op^1][j][k][1],f[op][j][k][1]+=f[op^1][j][k][2]+f[op^1][j][k][0];
}
for(int j=0;j<3;j++)
for(int k=0;k<2;k++)
for(int q=0;q<3;q++) f[op][j][k][q]%=mod;
}
printf("%lld",(f[op][2][0][0]+f[op][2][1][0])%mod);
}