[CF] B. Ugly Pairs - Educational Codeforces Round 64 (Rated for Div. 2)
Solutions Codeforces 构造 1800 Med
Lastmod: 2025-02-05 周三 00:27:45

https://codeforces.com/contest/1156/problem/B

题目大意

给出小写字母组成的字符串 S,要求重排字符串使得相邻位置,不能是字典序相邻的字符。(‘a’ 与 ‘z’ 不相邻)。有解时输出任意合法串,否则输出 ‘No answer’。

|S|100

简要题解

观察:

  1. 相同字符连在一起是合法的
  2. 如果按 c - ‘a’ 的奇偶性分组那么每组内也是合法的。因为这样字符之间的距离至少为 2
  3. 当字符种类足够多时,奇偶分完类总能有某种拼接使得结果合法

现在主要是看 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 不同的开头结尾相连的所有情况。

复杂度

TO(n)

SO(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;
}
Prev: [CF] A. Inscribed Figures - Educational Codeforces Round 64 (Rated for Div. 2)
Next: [CF] E. Special Segments of Permutation - Educational Codeforces Round 64 (Rated for Div. 2)

Gitalking ...