对于第
i
i
i组匹配的
a
a
a和
b
b
b,我们可以将按位置将它们称为
(
p
i
,
q
i
)
(p_i,q_i)
(pi,qi)。
将原序列中的
a
a
a看做
1
1
1,
b
b
b看做
−
1
-1
−1,那么前缀和为
0
0
0的位置就将序列分为了若干段。显然每组
p
i
p_i
pi和
q
i
q_i
qi都在同一段中,且同一段中相应的
p
i
p_i
pi和
q
i
q_i
qi的大小关系不变。我们称
p
i
>
q
i
p_i>q_i
pi>qi的段为
A
A
A类,否则为
B
B
B类。
考虑对于一个
A
A
A类的段,我们其中选择最早的是第
i
i
i组,那么可以删去所有
<
i
<i
<i的组。由于
p
i
>
q
i
p_i>q_i
pi>qi,因此相应的
a
a
a在
b
b
b后面,并且易知
p
i
p_i
pi和
q
i
q_i
qi之间没有被删去的都是某一组开头的
b
b
b。为了字典序尽量大,我们显然会将这些
b
b
b全选,于是又会选后面的若干个
a
a
a。这样推下去我们会将该段内所有
≥
i
\geq i
≥i的组都选了(因为中间前缀和不为
0
0
0,所以刚刚的过程在段尾之前不会结束)。于是对于一个长度为
l
e
n
len
len的
A
A
A类段,我们只有
l
e
n
/
2
len/2
len/2种可能的选法,分别暴力求出来即可。
对于一个
B
B
B类的段,我们如果在其中选了某些组,那么开头一定是
a
a
a。所以如果后面有
A
A
A类段我们一定不会选,否则我们的选择显然形如
a
b
a
b
a
b
.
.
.
ababab...
ababab...,且会让
a
b
ab
ab尽量多,这个贪心选择最前面能选的即可。
最后简单从后往前合并一下每段答案即可。时间复杂度
O
(
N
2
)
\mathcal O(N^2)
O(N2)。
#include <bits/stdc++.h>
using namespace std;
string str,f[3005];
int pos[3005];
bool kind[3005];
int bel[6005];
int main() {
int n;
cin>>n>>str;
int cnt=0,s=0;
for(int i=0;i<2*n;i++) {
s+=((str[i]=='b')?1:-1);
if (!s) {
pos[++cnt]=i;
kind[cnt]=((str[i]=='a')?0:1);
}
}
pos[0]=-1;
bool v=1;
for(int i=cnt;i>0;i--)
if (!kind[i]) {
v=0;
int s1=0,s2=0;
for(int j=pos[i-1]+1;j<=pos[i];j++)
if (str[j]=='b') bel[j]=++s1; else bel[j]=++s2;
f[i]=f[i+1];
for(int j=1;j<=s1;j++) {
string cur;
for(int k=pos[i-1]+1;k<=pos[i];k++)
if (bel[k]>=j) cur+=str[k];
f[i]=max(f[i],cur+f[i+1]);
}
}
else if (v) {
int s1=0,s2=0;
for(int j=pos[i-1]+1;j<=pos[i];j++)
if (str[j]=='a') bel[j]=++s1; else bel[j]=++s2;
int r=pos[i-1]+1;
for(;;) {
while (r<=pos[i]&&str[r]=='b') r++;
if (r>pos[i]) break;
f[i]+="ab";
int t=bel[r];
r++;
while (bel[r]!=t) r++;
r++;
}
f[i]+=f[i+1];
}
else f[i]=f[i+1];
cout<<f[1]<<endl;
return 0;
}