Description
给出一个长度为N由B、W、X三种字符组成的字符串S,你需要把每一个X染成B或W中的一个。
对于给出的K,问有多少种染色方式使得存在整数a,b,c,d使得:
1<=a<=b<c<=d<=N
Sa,Sa+1,...,Sb均为B
Sc,Sc+1,...,Sd均为W
其中b=a+K-1,d=c+K-1
由于方法可能很多,因此只需要输出最后的答案对109+7取模的结果。
Input
第一行两个正整数N,K
第二行一个长度为N的字符串S
Output
一行一个整数表示答案%(109+7)。
Sample Input
5 2
XXXXX
Sample Output
4
数据约定
对于20%的数据,N<=20
对于50%的数据,N<=2000
对于100%的数据,1<=N<=10^6,1<=K<=10^6
HINT
Source
乱dp
最先我考虑的是dp[i][3]表示当前第i位,然后状态为0/1/2的方案数
发现会算重
变成dp[i][3][2]表示当前这一位是'B'还是'W' 然后只转移第一次能转移的 一定是从'B'之后放K个'W'
还有一个问题 会算重 如中间k=3染成'BBB'仍然会计算成0的状态 那么我们提前减掉就行了
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
const int mod = 1e9 + 7;
char s[maxn];
bool b[maxn], w[maxn];
int n, m;
int dp[maxn][3][2];
int vis[2];
inline void upd(int &x, int y) { x += y; if( x >= mod ) x -= mod; if( x < 0 ) x += mod; }
int main()
{
scanf( "%d%d", &n, &m );
scanf( "%s", s + 1 );
for( int i = 1 ; i <= n ; i++ )
{
if( s[ i ] == 'W' ) vis[ 0 ] ++;
if( s[ i ] == 'B' ) vis[ 1 ] ++;
if( i >= m )
{
if( !vis[ 0 ] ) b[ i - m + 1 ] = 1;
if( !vis[ 1 ] ) w[ i - m + 1 ] = 1;
if( s[ i - m + 1 ] == 'W' ) vis[ 0 ] --;
if( s[ i - m + 1 ] == 'B' ) vis[ 1 ] --;
}
}
dp[ 0 ][ 0 ][ 1 ] = 1;
for( int i = 0 ; i < n ; i++ )
{
if( b[ i + 1 ] ) upd( dp[ i + m ][ 1 ][ 0 ], dp[ i ][ 0 ][ 1 ] ),
upd( dp[ i + m ][ 0 ][ 0 ], -dp[ i ][ 0 ][ 1 ] );
if( w[ i + 1 ] ) upd( dp[ i + m ][ 2 ][ 1 ], dp[ i ][ 1 ][ 0 ] ),
upd( dp[ i + m ][ 1 ][ 1 ], - dp[ i ][ 1 ][ 0 ] );
for( int j = 0 ; j <= 2 ; j++ )
for( int k = 0 ; k < 2 ; k++ )
{
if( s[ i + 1 ] != 'B' )
upd( dp[ i + 1 ][ j ][ 1 ], dp[ i ][ j ][ k ] );
if( s[ i + 1 ] != 'W' )
upd( dp[ i + 1 ][ j ][ 0 ], dp[ i ][ j ][ k ] );
}
}
upd( dp[ n ][ 2 ][ 0 ], dp[ n ][ 2 ][ 1 ] );
cout << dp[ n ][ 2 ][ 0 ] << endl;
}