大致题意: 给定一个由"X","B","W"三种字符组成的字符串,让你把所有"X"替换为"B"或"W",使得存在一段长度为(m)、全是"B"的子串,且其后还存在一段长度为(m)、全是"W"的子串。求方案数。
动态规划
发现现在做题总是把题目想复杂,其实这道题还是比较简单的。。。
我们设(f_{i,0/1},g_{i,0/1},h_{i,0/1})分别表示在前(i)个字符中不存在"B"的子串、存在"B"的子串但其后不存在"W"的子串、满足了题目的要求,且最后一个选择的字符是"B"/"W"的方案数。
首先,如果这一位上不是"B"(即是"X"或"W"),那么我们可以直接从上一位转移得到(f_{i,0},g_{i,0},h_{i,0}),同理在这一位上不是"W"时可以直接转移得到(f_{i,1},g_{i,1},h_{i,1})。
但仅仅这样转移肯定是不够的,我们还需要考虑从一种状态到另一种状态的过渡转移。
即,如果之前的(m)位不存在"W"(即可以全部染成"B"),我们就令(f_{i,1})减去(f_{i-m,0}),(g_{i,1})加上(f_{i-m,0})。
注意要从第二维为(0)的情况转移,是为了避免一段长度大于(m)的子串被计算多次。
同理,如果之前的(m)为不存在"B",我们就令(g_{i,0})减去(g_{i-m,1}),(h_{i,0})加上(g_{i-m,1})。
于是这道题就做完了,最终答案就是(h_{n,0}+h_{n,1})。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define X 1000000007
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
#define Dec(x,y) ((x-=(y))<0&&(x+=X))
using namespace std;
int n,m,f[N+5][2],g[N+5][2],h[N+5][2],s[N+5][2];char st[N+5];
int main()
{
RI i;for(scanf("%d%d%s",&n,&m,st+1),i=1;i<=n;++i)
s[i][0]=s[i-1][0],s[i][1]=s[i-1][1],st[i]^'X'&&++s[i][st[i]=='B'];//预处理前缀和以判断一段区间内是否存在某个数
for(f[0][0]=i=1;i<=n;++i)//初始化时默认第0位填0
st[i]^'B'&&(f[i][0]=(f[i-1][0]+f[i-1][1])%X,g[i][0]=(g[i-1][0]+g[i-1][1])%X,h[i][0]=(h[i-1][0]+h[i-1][1])%X),//可填0
st[i]^'W'&&(f[i][1]=(f[i-1][0]+f[i-1][1])%X,g[i][1]=(g[i-1][0]+g[i-1][1])%X,h[i][1]=(h[i-1][0]+h[i-1][1])%X),//可填1
i>=m&&s[i-m][0]==s[i][0]&&(Dec(f[i][1],f[i-m][0]),Inc(g[i][1],f[i-m][0])),//可以得到一段B串
i>=m&&s[i-m][1]==s[i][1]&&(Dec(g[i][0],g[i-m][1]),Inc(h[i][0],g[i-m][1]));//可以得到一段W串
return printf("%d
",(h[n][0]+h[n][1])%X),0;//输出答案
}