Easy
题目描述
给你一个由小写英文字母组成的字符串 s 和一个整数 k。
你的任务是删除字符串中的一些字符(可能不删除),使得结果字符串中的不同字符数量最多为 k。
返回实现此目标所需的最少删除次数。
示例 1:
输入:s = "abc", k = 2
输出:1
解释:
s 有三个不同的字符:'a'、'b' 和 'c',每个字符的频率都是 1。
由于我们最多可以有 k = 2 个不同字符,需要从字符串中删除任意一个字符的所有出现次数。
例如,删除所有 'c' 的出现次数,结果最多有 k 个不同字符。因此,答案是 1。
示例 2:
输入:s = "aabb", k = 2
输出:0
解释:
s 有两个不同的字符('a' 和 'b'),频率分别为 2 和 2。
由于我们最多可以有 k = 2 个不同字符,不需要删除。因此,答案是 0。
示例 3:
输入:s = "yyyzz", k = 1
输出:2
解释:
s 有两个不同的字符('y' 和 'z'),频率分别为 3 和 2。
由于我们最多可以有 k = 1 个不同字符,需要从字符串中删除任意一个字符的所有出现次数。
删除所有 'z' 的出现次数,结果最多有 k 个不同字符。因此,答案是 2。
约束条件:
1 <= s.length <= 161 <= k <= 16s只包含小写英文字母
解题思路
解题思路
这是一个经典的贪心问题。核心思想是:当字符串中不同字符的数量超过 k 时,我们需要删除一些字符,使得剩余的不同字符数量不超过 k。
贪心策略分析:
- 统计频次:首先统计字符串中每个字符出现的频次。
- 贪心选择:为了使删除次数最少,我们应该删除那些出现频次最少的字符。这是因为删除频次少的字符能够以最小的代价减少不同字符的数量。
- 排序优化:将各字符的频次按升序排列,然后依次删除频次最小的字符,直到不同字符数量不超过 k。
算法步骤:
- 使用哈希表统计每个字符的出现频次
- 将所有频次值收集到数组中并排序
- 计算需要删除的字符种类数:
d = 不同字符数 - k - 如果
d <= 0,说明无需删除,返回 0 - 否则,删除前 d 个频次最小的字符,返回它们的频次之和
这种贪心策略是最优的,因为每次删除都选择代价最小的选项。
代码实现
class Solution {
public:
int minDeletion(string s, int k) {
unordered_map<char, int> freq;
for (char c : s) {
freq[c]++;
}
vector<int> counts;
for (auto& p : freq) {
counts.push_back(p.second);
}
sort(counts.begin(), counts.end());
int distinctChars = counts.size();
int d = distinctChars - k;
if (d <= 0) {
return 0;
}
int deletions = 0;
for (int i = 0; i < d; i++) {
deletions += counts[i];
}
return deletions;
}
};
class Solution:
def minDeletion(self, s: str, k: int) -> int:
from collections import Counter
freq = Counter(s)
counts = list(freq.values())
counts.sort()
distinct_chars = len(counts)
d = distinct_chars - k
if d <= 0:
return 0
return sum(counts[:d])
public class Solution {
public int MinDeletion(string s, int k) {
var freq = new Dictionary<char, int>();
foreach (char c in s) {
freq[c] = freq.GetValueOrDefault(c, 0) + 1;
}
var counts = new List<int>(freq.Values);
counts.Sort();
int distinctChars = counts.Count;
int d = distinctChars - k;
if (d <= 0) {
return 0;
}
int deletions = 0;
for (int i = 0; i < d; i++) {
deletions += counts[i];
}
return deletions;
}
}
var minDeletion = function(s, k) {
const freq = new Map();
for (let c of s) {
freq.set(c, (freq.get(c) || 0) + 1);
}
const counts = Array.from(freq.values());
counts.sort((a, b) => a - b);
const distinctChars = counts.length;
const d = distinctChars - k;
if (d <= 0) {
return 0;
}
let deletions = 0;
for (let i = 0; i < d; i++) {
deletions += counts[i];
}
return deletions;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n + m log m) | n 是字符串长度,m 是不同字符数量(最多26个),主要消耗在排序 |
| 空间复杂度 | O(m) | 需要存储字符频次的哈希表和频次数组,m 是不同字符数量 |