https://codeforces.com/contest/1156/problem/B
题目大意
给出小写字母组成的字符串 $S$,要求重排字符串使得相邻位置,不能是字典序相邻的字符。(‘a’ 与 ‘z’ 不相邻)。有解时输出任意合法串,否则输出 ‘No answer’。
$|S| \le 100$
简要题解
观察:
- 相同字符连在一起是合法的
- 如果按 $c - ‘a’$ 的奇偶性分组那么每组内也是合法的。因为这样字符之间的距离至少为 $2$。
- 当字符种类足够多时,奇偶分完类总能有某种拼接使得结果合法
现在主要是看 $3$。
只有一组时,总是成立的,以下讨论都不赘述。
设字符种类数为 $m$ 则当 $m \ge 5$ 时对于较多的组我们有 $max(c_i) - min(c_i) \ge 4$。也就是说,短的组的开头元素,不可能同时和长的一组的最大最小元素都距离为 $1$。
当 $m = 4$ 时,简单讨论一下,其实要么两组长度是 $1, 3$ 要么是 $2, 2$ 其实也总是有解的。但我们也可以将其归类为 $3$ 以下的可能无解的情况直接暴力。
void solve() {
string s; cin >> s;
map<char, int> cnt;
for (char c : s) cnt[c]++;
auto check = [&](char a, char b) -> bool {
return abs(a - b) == 1;
};
vector<char> vis;
for (auto [v, c] : cnt) {
vis.push_back(v);
}
int m = vis.size();
vector<char> ord;
if (m <= 3) {
vector<int> p(m);
iota(p.begin(), p.end(), 0);
int f = 0;
ord.resize(m);
do {
for (int i = 0; i < m; i++) {
ord[i] = vis[p[i]];
}
int ff = 1;
for (int i = 1; i < m; i++) {
if (check(ord[i - 1], ord[i])) {
ff = 0;
break;
}
}
if (ff) {
f = 1;
break;
}
} while (next_permutation(p.begin(), p.end()));
if (!f) {
cout << "No answer\n";
return;
}
} else {
vector<char> odd, even;
for (char c : vis) {
if ((c - 'a') & 1) {
odd.push_back(c);
} else {
even.push_back(c);
}
}
if (odd.empty()) {
ord = even;
} else if (even.empty()) {
ord = odd;
} else {
int f = 0;
int m0 = even.size(), m1 = odd.size();
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
if (!check(odd[m1 - 1], even[0])) {
f = 1;
break;
}
reverse(even.begin(), even.end());
}
if (f) break;
reverse(odd.begin(), odd.end());
}
for (char c : odd) ord.push_back(c);
for (char c : even) ord.push_back(c);
}
}
for (auto c: ord) {
int cc = cnt[c];
for (int i = 0; i < cc; i++) {
cout << c;
}
}
cout << '\n';
}
下面假定这两组字符为 $a, b$。
对于 $m = 4$,如果是 $1, 3$ 分,显然总有 $1$ 放在 $3$ 的那种开头或结束是成立的(和 $m \ge 5$ 是一样的)。如果是 $2, 2$ 分,假设四个值是 $a_0, a_1, b_0, b_1$,显然,如果 $|a_0 - b_0| \neq 1$ 或者 $|a_0 - b_1| \neq 1$ 我们已经找到了所有解,否则假设 $b_0 + 1 = a_0 = b_1 - 1$ 此时必有 $a_1 \le b_0 - 1$ 或者 $a_1 \ge b_1 + 1$。前者 $|a_1 - b_1| \neq 1$ 后者 $|a_1 - b_0| \neq 1$ 则总有解。
仔细研究一下,$m = 3$ 的不行的情况,其实对应了 $b_0 + 1 = a_0 = b_1 - 1$ 也就是三个字符是连续的情况,显然中间字符放在任何位置都是不合法的。其余情况如果 $a_0$ 放于中间合法,则放于两头也合法。
$m = 2$ 相当于只有一种放法。
我们注意到所有情况相当于都只需要检查 $a, b$ 不同的开头结尾相连的所有情况。
复杂度
$T$:$O(n)$
$S$:$O(n)$
代码实现
#include <bits/stdc++.h>
using namespace std;
int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();
using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;
template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> bool ckmin(T &x,const T &y) {
return y<x ? (x=y, true) : false; }
template<typename T> bool ckmax(T &x,const T &y) {
return x<y ? (x=y, true) : false; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z
if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
if(x<z) { y=x; x=z; } else if(y<z) y=z; }
// mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
// mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());
/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/
void solve() {
string s; cin >> s;
map<char, int> cnt;
for (char c : s) cnt[c]++;
auto check = [&](char a, char b) -> bool {
return abs(a - b) == 1;
};
vector<char> ord;
vector<char> odd, even;
for (auto [c, v] : cnt) {
if ((c - 'a') & 1) {
odd.push_back(c);
} else {
even.push_back(c);
}
}
int f = 0;
if (odd.empty()) {
ord = even;
f = 1;
} else if (even.empty()) {
ord = odd;
f = 1;
} else {
int m1 = odd.size();
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
if (!check(odd[m1 - 1], even[0])) {
f = 1;
break;
}
reverse(even.begin(), even.end());
}
if (f) break;
reverse(odd.begin(), odd.end());
}
for (char c : odd) ord.push_back(c);
for (char c : even) ord.push_back(c);
}
if (!f) {
cout << "No answer\n";
return;
}
for (auto c: ord) {
int cc = cnt[c];
for (int i = 0; i < cc; i++) {
cout << c;
}
}
cout << '\n';
}
int main() {
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
Next: [CF] E. Special Segments of Permutation - Educational Codeforces Round 64 (Rated for Div. 2)