【AtCoder】Forbidden Pattern

题目链接

分析

首先考虑哪些串能被删空。下面只考虑长度为偶数的串。考虑这样一个(错误的)算法:从左往右依次加入串中的字符,然后能删则删。这个算法对于结尾为 A 的串一定能删空。对称地,开头为 B 的串也一定能被删空。

现在只需要考虑开头为 A 结尾为 B 的串。如果它能被删空,则一定存在最早的一个时刻,使得串的开头为 B 或结尾为 A。考虑把串表示成 A XX .. XX B 的形式,若某个 XXAB 则肯定可以从开头/结尾一路删到它。

因此,如果能匹配下列模式串中至少一个,一个串就能被删空:

B XX XX .. X
X AB XX .. X
..
X XX .. AB X
X XX XX .. A

对于其它情况,每次删除可以等效于删除一个 XX,因此可以归纳出不能删空。

通过观察可以得到一个等价的条件:一个串能被删空,当且仅当它能被划分成若干个长为偶数的串,每个串的开头为 B 或结尾为 A。

接下来,我们可以考虑串 \(S\) 能否进行若干次删除得到 \(T\)

如果 \(T_1=\texttt{A}\),则可以找到 \(S\) 中的第一个位置 \(i\) 满足:

  • \(S_i=\texttt{A}\)
  • \(S_{1..i-1}\) 能被删空

然后去掉 \(S_{1..i}\)\(T_1\),继续做。

考虑反证,假设去掉之后不合法,而某个合法方案去掉的是 \(S_{1..j}\) (\(j>i\))。那么两种方案得到的 \(S\) 分别为:

XX .. XA XX ..
         XX ..

假设第二个串的某个前缀能被删空,那么第一个串右端点相同的前缀必然能被删空,矛盾。

如果 \(T_1=\texttt{B}\)\(S_1=\texttt{A}\),则可以找到最小的 \(2i\) 满足 \(S_{2i}=\texttt{A}\)\(S_{1..2i}\) 这段前缀没有用,可以直接删掉(证明可以考虑上述的一个串能被删空的等价条件)。

假设现在 \(S_1=\texttt{B}\)。如果 \(T_{1..2}=\texttt{BA}\),可以找到最小的 \(2i\) 满足 \(S_{2i}=\texttt{A}\),去掉 \(S_{1..2i}\)\(T_{1..2}\)。这一定合法,且 \(\texttt{A}\) 的位置一定最靠左。

现在只剩 \(T_{1..2}=\texttt{BB}\) 的情况,我们找到最小的 \(2i\) 满足 \(S_{2i}=\texttt{B}\),去掉 \(S_{1..2i-1}\)\(T_1\)。根据上述的一个串被删空的等价条件,我们实际想要去掉 \(T_1\) 以及 \(S\) 中由如下三段字符串拼成的一个前缀:

  • 一段能被删空的串
  • B
  • 一段能被删空的串

满足 \(S\) 删掉这个前缀之后开头为 B。那么能观察到这个删法是最短的。

实现

#include<bits/stdc++.h>
#define rp(i,a,b) for(int i=a,_=b;i<=_;++i)
#define pr(i,a,b) for(int i=a,_=b;i>=_;--i)
using namespace std;
const int mod=998244353;
inline int inc(int x,int y){
	return x+=y-mod,x+=x>>31&mod;
}
inline void upd(int &x,int y){
	x=inc(x,y);
}
const int N=1e6+9;
int n,x[N],a[N][2],f[N][4];
char s[N];
int main(){
	scanf("%d%s",&n,s+1);
	rp(i,1,n){
		x[i]=s[i]-'A';
	}
	rp(i,n+1,n+2)rp(j,0,1){
		a[i][j]=n+1;
	}
	pr(i,n,1){
		rp(j,0,1){
			a[i][j]=a[i+2][j];
		}
		a[i][x[i]]=i;
	}
	f[n+1][2]=1;
	f[n+1][3]=1;
	pr(i,n,1){
		f[i][0]=f[a[i][0]+1][3];
		if(x[i]){
			f[i][1]=~(n-i)&1;
			rp(j,0,1){
				upd(f[i][1],f[a[i+1][j]][j]);
			}
			f[i][2]=n-i&1;
		}else{
			f[i][1]=f[a[i+1][0]+1][1];
			f[i][2]=f[a[i+1][0]+1][2];
		}
		rp(j,0,2){
			f[i][3]=inc(f[i][3],f[i][j]);
		}
	}
	printf("%d\n",f[1][3]);
	return 0;
}
posted @ 2023-04-27 19:10  alfalfa_w  阅读(130)  评论(0编辑  收藏  举报