problem
solution
可以从 DFA\text{DFA}DFA 的思想来考虑这道题。
考虑建一个 DFA\text{DFA}DFA 只接受最后可以变成字符串 111 的原串。
因为每次是选择三个 连续/相邻 的位置操作,限制是比较强的,无非有三种情况。
case1
三个全 111,操作后只剩 111 个 111,相当于删去两个 111。case2
三个全 000,操作后只剩 111 个 000,相当于删去两个 000。case3
剩下的组合,操作后只剩 111 个 0/10/10/1,但都相当于删去 010101 各一个。
显然贪心地我们是不会操作 case1
减少 111 的数量的。而连续的 000 的数量也不会超过 222 个,超过一定可以操作回来。
只有 111 的数量大于等于 000 的数量就是合法的。
事实上最后情况 111 的数量绝对不可能跟 000 数量相同,因为题目保证输入串是奇数长度,而每次的操作都减少两个长度。
111 多了反正都会被接受,没必要在 DFA\text{DFA}DFA 上建立这么多状态。
我们将所有 111 的个数超过两个的状态都连回个数为 222 的状态,然后接受这个状态即可。
所以 DFA\text{DFA}DFA 的状态数是 1(0/1/2)−0(0/1/2)=3⋅3=91(0/1/2)-0(0/1/2)=3·3=91(0/1/2)−0(0/1/2)=3⋅3=9 种的。
最后就是在这个 DFA\text{DFA}DFA 上跑 dpdpdp 即可。
设 f(i,j,k):f(i,j,k):f(i,j,k): 到字符串的第 iii 位,此时状态为 jjj 个 111 和 kkk 个 000 的方案数。考虑向 i+1i+1i+1 位转移。
- si+1=0s_{i+1}=0si+1=0,直接增加 000 个个数即可。
- k=2k=2k=2,
case2
操作。f(i,j,2)→f(i+1,j,0)f(i,j,2)\rightarrow f(i+1,j,0)f(i,j,2)→f(i+1,j,0)。 - k≠2k\neq 2k=2,f(i,j,k)→f(i+1,j,k+1)f(i,j,k)\rightarrow f(i+1,j,k+1)f(i,j,k)→f(i+1,j,k+1)。
- k=2k=2k=2,
- si+1=1s_{i+1}=1si+1=1。在这个时候才考虑跟 000 的抵消情况。
- k=0k=0k=0,f(i,j,0)→f(i+1,min(j+1,2),0)f(i,j,0)\rightarrow f(i+1,\min(j+1,2),0)f(i,j,0)→f(i+1,min(j+1,2),0)。
- k≠0k\neq 0k=0,
case3
操作,f(i,j,k)→f(i,j,k−1)f(i,j,k)\rightarrow f(i,j,k-1)f(i,j,k)→f(i,j,k−1)。
- si+1=?s_{i+1}=?si+1=?,将上面两种情况都跑一遍即可。
code
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define int long long
#define maxn 300005
char s[maxn];
int n, ans;
int f[maxn][3][3];signed main() {scanf( "%s", s + 1 );n = strlen( s + 1 );f[0][0][0] = 1;//f[i][j][k]:到字符串第i位时 有j个1 k个0for( int i = 0;i < n;i ++ ) {if( s[i + 1] != '0' ) {for( int j = 0;j <= 2;j ++ ) {( f[i + 1][j][0] += f[i][j][1] ) %= mod;( f[i + 1][j][1] += f[i][j][2] ) %= mod;( f[i + 1][min( j + 1, 2ll )][0] += f[i][j][0] ) %= mod;}}if( s[i + 1] != '1' ) {for( int j = 0;j <= 2;j ++ ) {( f[i + 1][j][1] += f[i][j][2] ) %= mod;( f[i + 1][j][2] += f[i][j][1] ) %= mod;( f[i + 1][j][1] += f[i][j][0] ) %= mod;}}}for( int i = 0;i <= 2;i ++ )for( int j = 0;j <= i;j ++ )( ans += f[n][i][j] ) %= mod; printf( "%lld\n", ans );return 0;
}