对于第组匹配的和,我们可以将按位置将它们称为。
将原序列中的看做,看做,那么前缀和为的位置就将序列分为了若干段。显然每组和都在同一段中,且同一段中相应的和的大小关系不变。我们称的段为类,否则为类。
考虑对于一个类的段,我们其中选择最早的是第组,那么可以删去所有的组。由于,因此相应的在后面,并且易知和之间没有被删去的都是某一组开头的。为了字典序尽量大,我们显然会将这些全选,于是又会选后面的若干个。这样推下去我们会将该段内所有的组都选了(因为中间前缀和不为,所以刚刚的过程在段尾之前不会结束)。于是对于一个长度为的类段,我们只有种可能的选法,分别暴力求出来即可。
对于一个类的段,我们如果在其中选了某些组,那么开头一定是。所以如果后面有类段我们一定不会选,否则我们的选择显然形如,且会让尽量多,这个贪心选择最前面能选的即可。
最后简单从后往前合并一下每段答案即可。时间复杂度。
#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;
}
来源:CSDN
作者:mayaohua2003
链接:https://blog.csdn.net/qq_38609262/article/details/103481984