AGC022E Median Replace

Median Replace

有个奇数长度的01串(s),其中有若干位置是?。

每次可将(3)个连续的字符替换成这三个数的中位数。

求有多少方案将?替换成0/1使得进行(frac{N-1}{2})次操作后的字符串是1?

(1leq |S|leq 300000)

题解

仓鼠《动态规划选讲》。

很多这样的题,套路就是先考虑一个判定的过程,然后把过程写到DP里面去就可以了。所有我们先考虑判定的过程。

维护一个栈,这个栈满足存在一个分界点,前面全是1后面全是0。这里前面后面指的是,把栈从栈底向栈顶写下来。然后从左往右扫。

如果当前是0,就把0压入到栈里面去。如果有连续的三个0,可以把这(3)个0消掉(2)个变成(1)个0。

如果当前是1,当栈顶是一个0的时候显然可以相互抵消(因为再找一个数取中位数,取决于再找的数),否则直接压入栈。

一个显然的事情是,栈里面0的个数不超过(2)。同时,当栈里面1的个数达到(2)时,那么最终一定可以让字符串变成1。因此,当1的个数超过(2)时,可以看成就是(2)

所以栈的种类数只有(3 × 3 = 9)种。

最终可以变成1的条件,就是最后的栈里面1个数不少于0个数。

直接把当前栈长成什么样记下来,从左往右DP即可。

时间复杂度是(O(N))

CO int N=3e5+10;
char str[N];
int F[N][3][3];

int main(){
	scanf("%s",str+1);
	int n=strlen(str+1);
	F[0][0][0]=1;
	for(int i=1;i<=n;++i){
		for(int j=0;j<=2;++j)for(int k=0;k<=2;++k){
			if(str[i]!='0'){
				if(k) chkadd(F[i][j][k-1],F[i-1][j][k]);
				else chkadd(F[i][min(j+1,2)][k],F[i-1][j][k]);
			}
			if(str[i]!='1'){
				if(k==2) chkadd(F[i][j][1],F[i-1][j][k]);
				else chkadd(F[i][j][k+1],F[i-1][j][k]);
			}
		}
	}
	int ans=0;
	for(int i=0;i<=2;++i)for(int j=0;j<=i;++j)
		chkadd(ans,F[n][i][j]);
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/12731582.html