Atcoder传送门
题目大意
给你一个长度为 N N 的字符串,只包含 0,1 0 , 1 和 x x 。可以视为 0 0 或。
现在你要执行 N−12 N − 1 2 次操作,每次选取连续的一段长度为 3 3 的字符串, 将其替换为其中数量较多的一个字符。例如, 换为 0 0 , 换为 1 1 , 而可换为 1 1 或。
现在请你输出经过 N−12 N − 1 2 次操作后,剩下的一个字符恰好为 1 1 的方案数(对取模)。
输入输出格式
输入格式
一行一个字符串 S S 。
输出格式
一个整数, 表示方案数(对 1000000007 1000000007 取模)。
数据范围
N≤300000 N ≤ 300000 , 保证 N N 为奇数。
输入输出样例
输入样例#1
1??00
输出样例#1
2
#### 输入样例#2
?0101???10???00?1???????????????0????????????1????0
输出样例#2
402589311
解题分析
毕克Dark♂佬讲的一道妙妙的题。考虑对01串建立自动机:
图中红边为接收到的转移, 绿边为接受到 0 0 的转移。
可以发现如果有出现, 我们总是可以先转移后面的字符串使最后前3个字符为 111 111 或 110 110 ,所以有一个自环。
注意到 100 100 是一种特殊情况, 因为 1000 1000 的时候我们并不是将前面三个字符 100 100 合并成 0 0 ,而是将合并为 0 0 , 所以这里要多加一个状态。
然后我们就可以在自动机上DP啦… 如果是就向两种字符都转移一下, 最后求一下 1 1 和状态的方案和就可以了。
复杂度 O(8×N) O ( 8 × N ) 。
代码如下:
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define ll long long
#define MX 300050
#define mod 1000000007
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
short go[8][2] = {{2, 1}, {4, 3}, {6, 5}, {3, 3}, {7, 1}, {2, 1}, {2, 2}, {4, 4}};//打表转移
ll dp[MX][8];
char buf[MX];
int len;
int main(void)
{
scanf("%s", buf + 1);
len = std::strlen(buf + 1);
dp[0][0] = 1;
R int j;
for (R int i = 0; i < len; ++i)
{
for (j = 0; j <= 7; ++j)
{
if(buf[i + 1] != '0') dp[i + 1][go[j][1]] += dp[i][j], dp[i + 1][go[j][1]] %= mod;
if(buf[i + 1] != '1') dp[i + 1][go[j][0]] += dp[i][j], dp[i + 1][go[j][0]] %= mod;
}
}
printf("%d\n", (dp[len][1] + dp[len][3]) % mod);
}