题目简述:

在一个一维数组中,存放着'0'、'1'、'2'、'*'、'?'这几个字符,数字代表他左右两边存在'*'的个数,'*'的左右两边可以是数字或者'*',而'?'代表这个地方的东西不确定是什么。题目问的是总共可能的情况有多少种,显而易见,不同的情况是由'?'所在的位置产生的。要我们输出所有的情况数,答案对1e9+7取模。


解决方案:

我们采用动态规划来解决这题。

首先是状态表示方程,我们用三维数组f[i][1/0][1/0]来表示方案数,第一个参数代表的是第i个格子,后面两个格子代表了当前格子和下一个格子的状态,一个f[i]有四种状态,代表了这个格子和下一个格子含有和不含有地雷的情况(含有地雷为1,不含有为0)。

这样我们就可以在状态转移的时候精准地从上一个格子继承应有的情况。


举个例子,如果当前格子内的字符是'1',那就代表这个格子没有地雷,而他的左右两边有且仅有一个地雷。

那么,这个格子可能的情况只有f[i][0][1]f[i][0][0]因为它自己不是地雷,所以第一个状态只能是零。

然后我们来看下一个格子,它也有两种情况:

第一种:地雷在右边,那么自然而然,左边就没有地雷,因此它就只有一种情况可以转移。所以,它的状态转移方程就是f[i][0][1]=f[i-1][0][0],(由此处可以发现状态表示方程设置成这样的好处,它正好可以表示所有的情况。)

再来看另一种情况,是地雷在左边的情况,此时它和它的右边是没有地雷的,所以两个状态都是零,而它继承的上一个必定是地雷,所以它也只继承一个情况,那么它的状态转移方程显而易见就是f[i][0][0]=f[i-1][1][0]

会了这个例子,相信其他的情况都是比较可以举一反三表示出来的。


Code:

#include<iostream>
#include<cstring>

using namespace std;

const int mod=1000000000+7;
int f[1000010][2][2];

int main(){
    string s;
    cin>>s;
    f[-1][0][1]=1;
    f[-1][0][0]=1;
    int n=s.size();
    for(int i=0;i<n;i++){
        if(s[i]=='0'){
            f[i][0][0]=f[i-1][0][0];
        }
        else if(s[i]=='1'){
            f[i][0][1]=f[i-1][0][0];
            f[i][0][0]=f[i-1][1][0];
        }
        else if(s[i]=='2'){
            f[i][0][1]=f[i-1][1][0];
        }
        else if(s[i]=='*'){
            f[i][1][0]=(f[i-1][0][1]+f[i-1][1][1])%mod;
            f[i][1][1]=f[i][1][0];
        }
        else if(s[i]=='?'){
            f[i][0][0]=(f[i-1][1][0]+f[i-1][0][0])%mod;
            f[i][0][1]=(f[i-1][1][0]+f[i-1][0][0])%mod;
            f[i][1][0]=(f[i-1][1][1]+f[i-1][0][1])%mod;
            f[i][1][1]=(f[i-1][0][1]+f[i-1][1][1])%mod;
        }
    }
    int res=0;
    res+=f[n-1][1][0]+f[n-1][0][0];
    res%=mod;
    cout<<res;
    return 0;
}