题目内容
给定一个长度为 n n n 的只有小写字母的字符串 s s s ,随意排列这个字符串,使得字符串正着和反着的最大字典序最小。
数据范围
- 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1≤n≤105
- s s s 中都是小写字母
题解
我们保证首尾字符尽可能相等,且字符尽可能小。
统计每个字符的出现次数。
记录最小字符 a a a 出现了 c a ca ca 次,大于最小字符 a a a 的字符 b b b 出现了 c b cb cb 次,大于 b b b 的字符 c c c 出现了 c c cc cc 次。
- c a = 1 , c b = 0 ca=1,cb=0 ca=1,cb=0 ,那么直接放
- c a ≥ 2 ca\geq 2 ca≥2 ,那么首尾放 a a a 即可
- c a = 1 , c b = 1 ca=1,cb=1 ca=1,cb=1 ,那么一边放 a a a 一边放 c c c 即可,剩余部分从 c c c 一边开始从小到大放
-
c
a
=
1
,
c
b
>
1
ca=1,cb>1
ca=1,cb>1
- c c = 0 cc=0 cc=0 ,那么两边放 b b b,把 a a a 留在最中间
-
c
c
>
0
cc>0
cc>0 ,那么一边放
a
a
a 一边放
b
b
b,剩余部分从
b
b
b 一边开始从小到大放。
c c > 0 cc>0 cc>0 如果不放 a a a ,那就是放两个 b b b 。
考虑放 a a a 与不放 a a a ,对于 a a a 这边来说不是决定最大字典序的模块,放了 a a a ,我就可以将剩余字符都按从小到大的顺序给 b b b 一边。但是如果不放 a a a ,我就要拿两个 b b b 放到两边,本来我的最小的是 b b . . . c c . . . d d . . . bb...cc...dd... bb...cc...dd... 这种形式,现在少了一个就成了 b c c . . . d d . . . bcc...dd... bcc...dd... 。那么这个 a a a 放到哪,后面一定是接一个从最大字符到最小字符的。但是我本来是 b b c c . . . d d . . . bbcc...dd... bbcc...dd... 的形式,变成了 b c c . . . d d . . . bcc...dd... bcc...dd... 形式,字典序就变大了,所以此时是一边放 a a a 一边放 b b b 最优解。
代码,
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
char s[N];
char ans[N];
int cnt[26];
int n;
void op(int& x, int& y, int i, int j) {
ans[x] = i + 'a';
ans[y] = j + 'a';
cnt[i] -= 1;
cnt[j] -= 1;
x += 1;
y -= 1;
}
void solve() {
memset(cnt, 0, sizeof cnt);
scanf("%s", s + 1);
n = strlen(s + 1);
if (n == 1) {
puts(s + 1);
return ;
}
for (int i = 1; i <= n; ++i) {
cnt[s[i] - 'a'] += 1;
}
ans[n + 1] = 0;
int i = 1, j = n;
while (i < j) {
int id[3] = {-1, -1, -1};
for (int c = 0; c < 26; ++c) {
if (cnt[c] > 0) {
if (id[0] == -1) id[0] = c;
else if (id[1] == -1) id[1] = c;
else if (id[2] == -1) id[2] = c;
else break;
}
}
if (id[0] != -1 && cnt[id[0]] >= 2) {
op(i, j, id[0], id[0]);
continue ;
}
if (id[0] != -1 && id[1] != -1 && cnt[id[0]] == 1 && cnt[id[1]] == 1) {
op(i, j, id[0], id[1]);
break;
}
if (id[2] == -1 && cnt[id[1]] >= 2) {
op(i, j, id[1], id[1]);
continue ;
} else {
op(i, j, id[0], id[1]);
break;
}
}
for (int k = i, c = 25; k <= j; ++k) {
while (c > 0 && cnt[c] == 0) c -= 1;
ans[k] = c + 'a';
cnt[c] -= 1;
}
reverse(ans + 1, ans + n + 1);
printf("%s\n", ans + 1);
}
int main()
{
int T = 1;
scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}