problem
solution
可以从 DFA \text{DFA} DFA 的思想来考虑这道题。
考虑建一个 DFA \text{DFA} DFA 只接受最后可以变成字符串 1 1 1 的原串。
因为每次是选择三个 连续/相邻 的位置操作,限制是比较强的,无非有三种情况。
case1
三个全 1 1 1,操作后只剩 1 1 1 个 1 1 1,相当于删去两个 1 1 1。case2
三个全 0 0 0,操作后只剩 1 1 1 个 0 0 0,相当于删去两个 0 0 0。case3
剩下的组合,操作后只剩 1 1 1 个 0 / 1 0/1 0/1,但都相当于删去 01 01 01 各一个。
显然贪心地我们是不会操作 case1
减少
1
1
1 的数量的。而连续的
0
0
0 的数量也不会超过
2
2
2 个,超过一定可以操作回来。
只有 1 1 1 的数量大于等于 0 0 0 的数量就是合法的。
事实上最后情况 1 1 1 的数量绝对不可能跟 0 0 0 数量相同,因为题目保证输入串是奇数长度,而每次的操作都减少两个长度。
1 1 1 多了反正都会被接受,没必要在 DFA \text{DFA} DFA 上建立这么多状态。
我们将所有 1 1 1 的个数超过两个的状态都连回个数为 2 2 2 的状态,然后接受这个状态即可。
所以 DFA \text{DFA} DFA 的状态数是 1 ( 0 / 1 / 2 ) − 0 ( 0 / 1 / 2 ) = 3 ⋅ 3 = 9 1(0/1/2)-0(0/1/2)=3·3=9 1(0/1/2)−0(0/1/2)=3⋅3=9 种的。
最后就是在这个 DFA \text{DFA} DFA 上跑 d p dp dp 即可。
设 f ( i , j , k ) : f(i,j,k): f(i,j,k): 到字符串的第 i i i 位,此时状态为 j j j 个 1 1 1 和 k k k 个 0 0 0 的方案数。考虑向 i + 1 i+1 i+1 位转移。
-
s
i
+
1
=
0
s_{i+1}=0
si+1=0,直接增加
0
0
0 个个数即可。
-
k
=
2
k=2
k=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 ≠ 2 k\neq 2 k=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
=
2
k=2
k=2,
-
s
i
+
1
=
1
s_{i+1}=1
si+1=1。在这个时候才考虑跟
0
0
0 的抵消情况。
- k = 0 k=0 k=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
≠
0
k\neq 0
k=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)。
- s i + 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个0
for( 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;
}