《C. Equal Frequencies》
这道题的题意为:一个字符串str上每个字母的数量都一样,为平衡字符串
现在给出一个字符串s,问最少改动多少使s变成平衡字符串,并写出该平衡字符串
这道题我之前一直想要找出一些规律,根本没往暴力方面想,现在想一下
一个平衡字符串上可能有1~26个字母
知道一个平衡字符串上有多少字母(这里假设为i个字母),就知道每个字母有多少个
知道每个字母有多少个(这里假设为x个,x=n/i),我们就可以贪心地
1.如果原来字符串s的字母数(num)>i
我们贪心地将字母数最大的i个字母保留,后面 num-i个字母都要变成前i个字母
同时前i个字母,如果字母数 > x,则要将多出来的部分补到少了的地方
2.如果原来字符串s的字母数(num)==i
如果字母数 > x,则要将多出来的部分补到少了的地方
3.如果原来字符串s的字母数(num)<i
如果字母数 > x,则要将多出来的部分补到少了的地方
上面的i我们可以从1~26枚举出了
然后比较得到最少操作数,与对应的平衡字符串上有i个字母时,改动最少
根据这两个信息我们可以对原字符串进行改动
#include <iostream> #include <algorithm> #include <cstring> #include <vector> using namespace std; struct node { int n = 0, c; bool operator<(const node &t) const { return n > t.n; } }; void solve() { int n; cin >> n; vector<node> a(26); string str; cin >> str; for (int i = 0; i < 26; i++) a[i].c = i; for (int i = 0; i < str.length(); i++) { int c = str[i] - 'a'; a[c].n++, a[c].c = c; } sort(a.begin(), a.end()); /* for (int i = 0; i < a.size(); i++) cout << a[i].n << " "; cout << endl; */ int ans = 1e9, cnt; for (int i = 1; i <= 26; i++) { int res = 0; if (n % i) continue; int x = n / i; // i>=原来字符串中字母个数的情况 // 将字母个数>x的字母要变成<x的字母; for (int j = 0; j < i; j++) res += (a[j].n > x ? a[j].n - x : 0); // 原来字符串中字母个数的情况>i的情况 // 只选择字母数排名前i的字母,排名大于i的字母全部变成需要的字母 for (int j = i; j < 26; j++) res += (a[j].n > 0 ? a[j].n : 0); if (ans > res) ans = res, cnt = i; } // 开始改变字符串的阶段: // 在cnt==原来字符串字母个数时,我们只要改字母数 // 在cnt>原来字符串字母个数时,我们还要引入新的字母 // 在cnt<原来字符串字母个数时,我们要将按照字母数排序的前cnt个字母给保留下来 // 将后面的字母都变成需要的字母 vector<char> s(n); vector<pair<int, int>> pos(26); for (int i = 0; i < a.size(); i++) { int c = a[i].c; pos[c] = {a[i].n, i + 1}; /* cout << char(c + 'a') << " " << a[i].n << " " << i + 1 << endl; */ } int need = n / cnt; /* cout << "need ans cnt: " << need << " " << cnt << endl; */ for (int i = 0; i < str.length(); i++) { int c = str[i] - 'a'; /* cout << "!: " << char(c + 'a') << " " << pos[c].first << " " << pos[c].second << endl; */ // 说明需要更换字母 if (pos[c].first > need || pos[c].second > cnt) { /* cout << "@: " << i << " " << "entry" << endl; */ // 在26个字母中找合法的字母 for (int j = 0; j < 26; j++) if (pos[j].first < need && pos[j].second <= cnt) { s[i] = j + 'a'; /* cout << "find: " << char(j + 'a') << " " << pos[j].first << " " << pos[j].second << endl; */ pos[j].first++; break; } if (pos[c].first > need) pos[c].first--; } else { /* cout << "@: " << i << " " << "entry2" << endl; */ s[i] = str[i]; /* cout << s[i] << " " << str[i] << endl; */ } } cout << ans << endl; for (int i = 0; i < s.size(); i++) cout << s[i]; cout << endl; } int main() { int t; cin >> t; while (t--) solve(); return 0; }