题目描述

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


输入格式

一行一个字符$s$。


输出格式

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


样例

样例输入:

?1?

样例输出:

2


数据范围与提示

对于$30\%$的数据,$|S|\leqslant 20$。
对于$60\%$的数据,$|S|\leqslant 1,000$。
对于$100\%$的数据,$|S|\leqslant {10}^6$。


题解

显然数据范围只允许我们$\Theta(n)$求解,那么也只能考虑$DP$了。

设$dp[i][j]$表示到了$i$位置,当前选了$j$,$j$分为五种情况,如下:

  $\alpha.$当前位置没有雷。

  $\beta.$当前位置左边有一个雷。

  $\gamma.$当前位置旁边有两个雷。

  $\delta.$当前位置有雷。

  $\epsilon.$当前位置左边有雷。

为方便,以上五种情况下面均以数字$0\sim 5$代替。

那么现在来考虑转移,首先是第一个位置,分为四种情况:

  $\alpha.$无雷:$dp[1][0]=1$。

  $\beta.$旁边有一个雷:$dp[1][4]=1$。

  $\gamma.$本身有雷:$dp[1][3]=1$.

  $\delta.$为$?$:$dp[1][0]=dp[1][3]=dp[1][4]=1$(第一个位置不可能出现$1,2$两种情况)。

那么我们在来考虑过程中的转移,依然分类讨论(注意以下''中为题目中给出的棋盘的状态,而不是我上面列出的对于五种情况的编号):

  $\alpha.'0':$可以由$0,1$两种情况转移得来,即为:$dp[i][0]=dp[i-1][0]+dp[i-1][1]$。

  $\beta.'1':$则还分两种情况,分别是$1$和$4$,那么式子为:$dp[i][1]=dp[i-1][3]$和$dp[i][4]=dp[i-1][0]+dp[i-1][1]$。

  $\gamma.'2':$只能由$3$转移得来,即:$dp[i][2]=dp[i-1][3]$。

  $\delta.'*':$可以由$2,3,4$三种情况转移得来,即:$dp[i][3]=dp[i-1][2]+dp[i-1][3]+dp[i-1][4]$。

  $\epsilon.'?'$包含以上所有情况。

最后来考虑如何统计答案,因为最后一位不可能是$2$和$4$两种情况,所以答案即为:$dp[n][0]+dp[n][1]+dp[n][3]$。

时间复杂度:$\Theta(n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
char ch[1000001];
long long dp[1000001][5];
int main()
{
scanf("%s",ch+1);
int n=strlen(ch+1);
switch(ch[1])
{
case '0':dp[1][0]=1;break;
case '1':dp[1][4]=1;break;
case '*':dp[1][3]=1;break;
case '?':dp[1][0]=dp[1][4]=dp[1][3]=1;break;
}
for(int i=2;i<=n;i++)
switch(ch[i])
{
case '0':dp[i][0]=(dp[i-1][0]+dp[i-1][1])%1000000007;break;
case '1':dp[i][1]=dp[i-1][3];dp[i][4]=(dp[i-1][0]+dp[i-1][1])%1000000007;break;
case '2':dp[i][2]=dp[i-1][3];break;
case '*':dp[i][3]=(dp[i-1][2]+dp[i-1][3]+dp[i-1][4])%1000000007;break;
case '?':
dp[i][0]=(dp[i-1][0]+dp[i-1][1])%1000000007;
dp[i][1]=dp[i-1][3];
dp[i][3]=(dp[i-1][2]+dp[i-1][3]+dp[i-1][4])%1000000007;
dp[i][4]=(dp[i-1][0]+dp[i-1][1])%1000000007;
dp[i][2]=dp[i-1][3];
break;
}
printf("%lld",(dp[n][0]+dp[n][1]+dp[n][3])%1000000007);
return 0;
}

rp++

最新文章

  1. 复旦高等代数 I(16级)思考题
  2. 读w3c中文教程对键盘事件解释的感想 -遁地龙卷风
  3. [知识点]Cantor展开
  4. centos 服务开机启动设置
  5. js定时器的一些小问题
  6. AFNetworking vs ASIHTTPRequest vs MKNetworkKit
  7. [Hibernate] - many to one
  8. java数据结构和算法------合并排序
  9. HDOJ/HDU 1865 1sting(斐波拉契+大数~)
  10. Unix中$$、$@、$#、$*的意思
  11. &lt;php&gt;添加数据注意事项
  12. CSS3 制作向左、向右及关闭图标的效果
  13. python jquery
  14. Eclipse中查看JDK类库源代码
  15. swift 学习- 13 -- 下标
  16. PAT A1097 Deduplication on a Linked List (25 分)——链表
  17. python工具 - 从文件名读取特定信息到excel表格
  18. Redis为什么是单线程
  19. 微服务与DevOps关系
  20. How To Setup Apache Hadoop On CentOS

热门文章

  1. poj2236Wireless Network
  2. poj3253Fence Repair (Huffman)
  3. 4期Web安全基础
  4. Maven系列学习(二)Maven使用入门
  5. 自定义SAP搜索帮助记录-代码实现
  6. [fw]linux 下如何查看和踢除正在登陆的其它用户
  7. CSV的规范与使用
  8. git stash 后&quot;本地代码不见了&quot;
  9. C#设计模式:建造者模式(Builder Pattern)
  10. 同一客户端多个git账号的配置