Minimum Operations to Make Character Frequencies Equal
Minimum Operations to Make Character Frequencies Equal
You are given a string s
.
A string t
is called good if all characters of t
occur the same number of times.
You can perform the following operations any number of times:
- Delete a character from
s
. - Insert a character in
s
. - Change a character in
s
to its next letter in the alphabet.
Note that you cannot change 'z'
to 'a'
using the third operation.
Return the minimum number of operations required to make s
good.
Example 1:
Input: s = "acab"
Output: 1
Explanation:
We can make s
good by deleting one occurrence of character 'a'
.
Example 2:
Input: s = "wddw"
Output: 0
Explanation:
We do not need to perform any operations since s
is initially good.
Example 3:
Input: s = "aaabc"
Output: 2
Explanation:
We can make s
good by applying these operations:
- Change one occurrence of
'a'
to'b'
- Insert one occurrence of
'c'
intos
Constraints:
3 <= s.length <= 2 * 104
s
contains only lowercase English letters.
解题思路
这场爆 0 掉了一百多分,感觉最近主要是没怎么做题然后水平也不知道为什么会下滑这么严重,更重要的是心态出现很大的问题。
首先要想到可以枚举最终每个(存在)字符的出现次数,范围是 $1 \sim \max\limits_{0 \leq i < 26}\{c_i\}$,其中 $c_i$ 表示原串 $s$ 中第 $i$ 个字符的出现次数。容易贪心想到最终每个字符的出现次数不会比原本的最大出现次数还要多,这意味着需要做额外的操作 $1$,显然删掉这些多余的操作会使得总操作次数更少。所以现在的问题是当最终每个字符的出现次数是固定的 $x$ 时,我们怎么求出最少的操作次数?
当只有操作 $1$ 和操作 $2$ 时,每个字符的出现次数不是变成 $0$ 就是变成 $x$,可以独立求解。而操作 $3$ 本质是将对相邻字符的操作 $1$ 和操作 $2$(即 $c_i \gets c_i - 1$ 和 $c_{i+1} \gets c_{i+1} + 1$)合并起来代价变成 $1$,我们可以把问题看作利用操作 $3$ 来优化只有操作 $1$ 和操作 $2$ 的情况。
此时还需要观察到一个极其重要的性质,即当对 $c_i$ 使用过操作 $3$ 后,在最优解中一定不会再对 $c_{i+1}$ 使用操作 $3$。否则,假设对字符 $a$ 使用操作 $3$ 变成 $b$,然后又对 $b$ 使用操作 $3$ 变成 $c$。我们完全可以对 $a$ 使用操作 $1$,再对 $c$ 使用操作 $3$ 来取代。而如果通过操作 $3$ 来实现 $a \to b \to c \to d$,此时对 $a$ 使用操作 $1$,再对 $d$ 使用操作 $3$,只有 $2$ 次操作更优。以此类推。
所以当我们对 $c_i$ 使用过操作 $3$ 后,$c_{i+1}$ 只能使用操作 $1$ 或操作 $2$ 了,此时可以分类讨论。先考虑什么情况下 $c_i$ 可以使用操作 $3$。显然当 $c_{i+1} < x$ 时可以。如果 $c_{i+1} \geq x$ 呢,由于对 $c_i$ 使用操作 $3$ 后 $c_{i+1}$ 会增大,这将导致 $c_{i+1}$ 需要额外的操作 $1$ 来变小,显然此时对 $c_i$ 只使用操作 $1$ 会更优。因此只有当 $c_{i+1} < x$ 时才能对 $c_i$ 使用操作 $3$。接着再进行分类讨论:
- 将 $c_i$ 变成 $0$,此时需要对 $c_i$ 至多使用 $t = \min\{ c_i, x - c_{i+1} \}$ 次操作 $3$。如果 $t = c_i$,意味着接下来应该再使用 $x - c_{i+1} - t$ 次操作 $2$ 使 $c_{i+1}$ 变成 $x$,总的操作次数就是 $t + x - c_{i+1} - t = x - c_{i+1}$。否则 $t = x - c_{i+1}$,意味着接下来应该再使用 $c_i - t$ 次操作 $1$ 使 $c_i$ 变成 $0$,总的操作次数就是 $t + c_i - t = c_i$。综合起来总的操作次数就是 $\max\{c_i, x - c_{i+1} \}$。
- 将 $c_i$ 变成 $x$,显然需要满足 $c_i > x$。同样可以进行类似的分析,最终总的操作次数就是 $\max\{c_i - x, x - c_{i+1} \}$。
当对 $c_i$ 使用操作 $3$ 后,其实 $c_i$ 和 $c_{i+1}$ 就处理完成了,我们可以考虑剩余 $c_{i+2}, \ldots, c_{25}$ 的情况,为此可以进行 dp。定义 $f(i)$ 表示处理完 $c_{i}, \ldots, c_{25}$ 所需的最小操作次数,根据是否对 $c_i$ 使用操作 $3$ 进行划分。当不对 $c_i$ 使用操作 $3$,状态转移方程就是 $f(i) = \min\{f(i), \, f(i+1) + \min\{c_i, |x - c_i|\}\}$。当 $c_{i+1} < x$,有 $f(i) = \min\{f(i), \, f(i+2) + \max\{c_i, x - c_{i+1} \}\}$。当 $c_{i+1} < x$ 且 $c_i > x$,有 $f(i) = \min\{f(i), \, f(i+2) + \max\{c_i - x, x - c_{i+1} \}\}$。
AC 代码如下,时间复杂度为 $O(26 \, n)$:
class Solution {
public:
int makeStringGood(string s) {
vector<int> c(26);
for (auto &x : s) {
c[x - 'a']++;
}
int m = *max_element(c.begin(), c.end()), ret = 1e9;
for (int i = 1; i <= m; i++) {
vector<int> f(27);
for (int j = 25; j >= 0; j--) {
f[j] = f[j + 1] + min(c[j], abs(i - c[j]));
if (j + 1 < 26 && c[j + 1] < i) {
f[j] = min(f[j], f[j + 2] + max(c[j], i - c[j + 1]));
if (c[j] > i) f[j] = min(f[j], f[j + 2] + max(c[j] - i, i - c[j + 1]));
}
}
ret = min(ret, f[0]);
}
return ret;
}
};
日记
今天是我生日,又是与往年一样一个人度过呢。虽说习惯了孤独,但能与有同好的人一起交流,未尝不是一件好事。前天跟许久未见的高中同学见了面,吃个了午餐然后去大商场摸机子,看似很平常的行为,可对我来说是从未有过的体验。读书期间一直待在学校基本没出去过,更别说出来玩了。非常感谢我的高中同学,虽然对他来说那也许只是个再普通不过的下午,但对我而言却成了美好的回忆。Happy birthday to me.
参考资料
思维+DP【力扣周赛 428】:https://www.bilibili.com/video/BV1pnqZYKEqr/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18611174