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(4∗n)
但对于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;
}