题目链接
先考虑对于一个给定的字符串如何判断是否合法。从前往后考虑一个个字符加进来的情况,注意到一些奇妙的性质:1.若当前开头有2个1,则必合法;2.对于相连的01或10,可选择忽视(和任何一个数结合结果都是那个数)。
考虑把性质1作为目标,则01直接忽视,10先保留,这样当前序列一定是一些1后接着一些0,只需判断最后的序列是否不少于2个1,或只有1个1而没有0。
对于问号,考虑DP记录状态,1的状态只有三种(大于等于2都一样),连续的3个0显然缩为一个更优,于是0也只有三种状态,效率
O
(
n
)
O(n)
O(n)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5,P=1e9+7;
char a[N]; int n,f[N][3][3];
int main()
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%s",a+1); n=strlen(a+1); f[0][0][0]=1;
for(int i=0;i<n;i++)
for(int j=0;j<3;j++) for(int k=0;k<3;k++) if(f[i][j][k]){
int v=f[i][j][k];
for(int x=0;x<2;x++) if(x==a[i+1]-'0' || a[i+1]=='?'){
if(j){
int y;
if(!x){
y=j+1; if(y==3) y=1;
}
else y=j-1;
(f[i+1][y][k]+=v)%=P;
}
else{
if(k==2) (f[i+1][0][2]+=v)%=P;
else{
if(!x) (f[i+1][1][k]+=v)%=P;
else (f[i+1][0][k+1]+=v)%=P;
}
}
}
}
int ans=f[n][0][1];
for(int i=0;i<3;i++) (ans+=f[n][i][2])%=P;
cout<<ans<<endl;
return 0;
}