Mine

Mine

Description

有一个 1 维的扫雷游戏,每个格子用*表示有雷,用 0/1/2 表示无
雷并且相邻格子中有 0/1/2 个雷。
给定一个仅包含?、*、0、1、2 的字符串 s,问有多少种方法将所
有的?改为*/0/1/2 使其合法。

Input

一行一个字符串 s。

Output

一行一个整数表示答案,对 10^9+7 取模。

Input 示例

?1?

Output 示例

2

数据范围

对于 30%的数据,|S|<=20。
对于 60%的数据,|S|<=1000。
对于 100%的数据,|S|<=10^6。

Solution

先讲最暴力的方法,对于每一位‘?’枚举该位填入什么,最后判断,时间复杂度为 O( 4^{|S|} ) ,然而一看只有十分啊,所以这个显然是不行的,但是我们发现除了 当前一位填‘1’以外,填其他数字都可以确定左边和右边格子的情况,所以可以自然而然的想到dp,设dp[i][j][k]为填到第i位,前面一位填j,当前一位填k的方案数,这样的时间复杂度是 O( 4^2 |S|) ,这样的时间复杂度其实已经可以卡过这道题,但我们再想一想可以发现,其实这样dp很多状态都是无意义的,所以我们可以把它展开,发现对于每一位最多只有五中状态,这样子复杂度就成 O(4n) 但对于dp状态的初始化,我们需要通过S的第一位来分别进行,代码如下:
#include<cstdio>
#include<algorithm>
#include<cstring>
#define Mod 1000000007
using namespace std;
char ch[1000010];
int dp[1000010][4][2];
int main(){
    freopen("mine.in","r",stdin);
    freopen("mine.out","w",stdout);
    scanf("%s",ch+1);
    int l=strlen(ch+1);
    if (ch[1]=='*') dp[0][2][1]=1;
    else if (ch[1]=='?') dp[0][2][1]=1,dp[0][1][0]=1;
    else dp[0][1][0]=1;
    for (int i=1;i<=l;i++){
        if (ch[i]=='?'){
            dp[i][0][0]=((dp[i-1][2][1]+dp[i-1][3][0])%Mod+dp[i-1][0][0])%Mod;
            dp[i][1][0]=(dp[i-1][1][0]+dp[i-1][2][0])%Mod;
            dp[i][2][0]=dp[i-1][0][0];
            dp[i][2][1]=(dp[i-1][2][0]+dp[i-1][1][0])%Mod;
            dp[i][3][0]=dp[i-1][0][0];
        }
        else{
            if (ch[i]=='*'){
                dp[i][0][0]=((dp[i-1][2][1]+dp[i-1][3][0])%Mod+dp[i-1][0][0])%Mod;
            }
            if (ch[i]=='0'){
                dp[i][1][0]=(dp[i-1][1][0]+dp[i-1][2][0])%Mod;
            }
            if (ch[i]=='1'){
                dp[i][2][0]=dp[i-1][0][0];
                dp[i][2][1]=(dp[i-1][2][0]+dp[i-1][1][0])%Mod;
            }
            if (ch[i]=='2'){
                dp[i][3][0]=dp[i-1][0][0];
            }
        }
    }
    int ans=0;
    ans=((dp[l][0][0]+dp[l][1][0])%Mod+dp[l][2][0])%Mod;
    printf("%d\n",ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值