[AGC022E] Median Replace 题解

题意

对于一个(mathtt{01})串我们每次可将(3)个连续的字符替换成这三个数的中位数,如果最后可以得到长度为(1)的字符串(mathtt{1}),我们称这个字符串是漂亮的。

现有(1)个长度为奇数的(mathtt{01})(s) 其中有若干位置是(?),表示该字符可以被替换为(mathtt{0,1})中的任意一个字符,求这个字符串可以表示多少个不同的漂亮的字符串,答案对 $ 10^{9} + 7 $ 取模。

(|S|le 300000)

题解

本篇题解基本上为yhx大佬的题解的总结,更详细的题解请参见大佬的博客。

这类字符串问题常常需要我们写出状态的转移数组再进行DP。

1.操作分类

我们首先考虑如何判断一个串是否是漂亮的。

我们发现操作可以分为三种:

1.( exttt{000} o exttt{0})

2.去掉相邻的一对 ( exttt 0, exttt 1)

3.( exttt{111} o exttt{1})

2.操作优先级

可以发现这三类操作有优先级,优先级为(1>2>3)

我们考虑如何证明这个优先级。

首先,容易发现,如何一个串(S)是漂亮的,那么将(S)中的一些(mathtt{0})替换为(mathtt{1})后其仍是漂亮的,因为我们仍然可以按照原先的操作序列进行操作。

如果当前字符串为(S = a cdot exttt{0001} cdot b) ,假设我们通过操作2得到的(S' = a cdot exttt{00} cdot b)是漂亮的,那么我们通过操作1得到的(S'' = a cdot exttt{01} cdot b)也是漂亮的,即通过操作2可以得到的合法操作序列通过操作1也一定能得到,操作1优先于操作2,运用类似的方法可以证明操作2优先于操作3。

3.贪心判断算法

我们现在得到了一个贪心的判断算法:

1.维护一个栈,如果栈顶三个字符为(mathtt{000}),则弹出其中两个(mathtt{0})

2.如果栈顶为(mathbb{1}),次栈顶为(mathtt{0}),也立即弹出两个字符;如果栈顶为(mathbb{0}),次栈顶为(mathtt{1}),就先留着,因为可能会遇到连续三个(mathtt{0})的更优情况

3.在这种贪心的过程中,一旦栈内出现了连续两个(mathtt{1}),就可以说明该字符串一定是漂亮的,因为我们可以先把后面的合并了再合并这两个(1),从而得到(1)

自此我们得到了(O(n))的判断算法。

4.自动机跑匹配

我们发现栈内状态及弹出的过程很像自动机的状态及转移,于是我们可以写出状态的转移数组再进行自动机上的DP。

自动机如下(S表示空节点):

DFA.jpg

我们在自动机上跑匹配即可。

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
const int mod=1e9+7;
#define add(a,b) a=(a+b)%mod
char s[N];
int f[N][7],g[2][7]={{1,3,4,1,6,5,4},{2,0,5,1,2,5,4}};
int main(){
	scanf("%s",s+1);
	int n=strlen(s+1);
	f[0][0]=1;
	for(int i=0;i<n;i++)
		for(int j=0;j<7;j++){
			if(s[i+1]!='1')add(f[i+1][g[0][j]],f[i][j]);
			if(s[i+1]!='0')add(f[i+1][g[1][j]],f[i][j]);
		}
	printf("%d
",(f[n][2]+f[n][5])%mod);
	return 0;
} 
原文地址:https://www.cnblogs.com/Robert-JYH/p/14800069.html