AGC026E 题解

First Post:

Last Update:

思路并不困难,重点在于可能的出发点很多,但要快速地找到最适合的那个。

下文用 表示第 的出现位置。

看到这种字典序最大的串,第一反应是从前到后按位贪心。但直接枚举结果串的每一位,然后判能否被原串生成是显然不好做的。注意到操作对原串的限制其实不弱,不妨从原串的每一对 入手来考虑问题。

然后发现简单贪心其实不太好做,但你发现问题基本上是分阶段的,可以考虑 DP。

所以设 表示只考虑第 与第 的最大结果串。

首先 。然后考虑保留第 后面怎么决策。如果 前面,那么这中间的所有 对应的编号显然都比 小,不能保留;这中间的所有 保留了会更劣,也不能保留。所以此时找到最小的 使得 (其中 表示字符串拼接)。

如果 前面,情况有所不同。显然夹在中间的所有 都必须保留,那么这些”必须保留的 ”也会给后面一个限制,那么我们找到其中编号最大的 ,设为 。那么接着找到 之间编号最大的 ,将 赋值为这个 。一直做下去直到 之间全是 。那么编号在 中间的 对都必须保留。我们用 在前面接上 对形成的串即可。

时间复杂度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <bits/stdc++.h>
using namespace std;
const int N = 3e3 + 5;
int n;
char s[N << 1];
int pa[N],pb[N];
int Id[N << 1];
string dp[N];
int main() {
scanf("%d%s",&n,s + 1);
for(int i = 1,ca = 0,cb = 0;i <= n + n;i++)
if(s[i] == 'a') pa[++ca] = i,Id[i] = ca; else pb[++cb] = i,Id[i] = cb;
for(int i = n;i >= 1;i--) {
dp[i] = dp[i + 1];
if(pa[i] < pb[i]) {
int ps = i + 1;
while(ps <= n && pa[ps] <= pb[i]) ++ps;
string v = "ab" + dp[ps];
if(v > dp[i]) dp[i] = v;
} else {
int now = i;
while(1) {
int mx = 0;
for(int i = pb[now] + 1;i < pa[now];i++)
if(s[i] == 'b') mx = Id[i];
if(!mx) break;
now = mx;
}
string t = dp[now + 1],t2;
for(int j = i;j <= pa[now];j++) if(Id[j] >= i) t2 += s[j];
t = t2 + t;
if(t > dp[i]) dp[i] = t;
}
}
string ans;
for(int i = 1;i <= n;i++) if(dp[i] > ans) ans = dp[i];
cout << ans << endl;
return 0;
}