【每日一题】LeetCode 2516.每种字符至少取k个(哈希表、字符串、滑动窗口)
题目描述
给定一个由字符 ‘a’、‘b’、‘c’ 组成的字符串 s
和一个非负整数 k
。每分钟,你可以选择取走 s
最左侧或最右侧的那个字符。
你必须取走每种字符至少 k
个,返回需要的最少分钟数;如果无法取到,则返回 -1
。
输入示例
示例 1
- 输入:
s = "aabaaaacaabc", k = 2
- 输出:
8
- 解释:从
s
的左侧取三个字符,现在共取到两个字符 ‘a’ 、一个字符 ‘b’ 。从s
的右侧取五个字符,现在共取到四个字符 ‘a’ 、两个字符 ‘b’ 和两个字符 ‘c’ 。共需要 3 + 5 = 8 分钟。
示例 2
- 输入:
s = "a", k = 1
- 输出:
-1
- 解释:无法取到一个字符 ‘b’ 或者 ‘c’,所以返回
-1
。
提示
1 <= s.length <= 10^5
s
仅由字母 ‘a’、‘b’、‘c’ 组成0 <= k <= s.length
思路分析
- 首先检查字符串长度是否小于
k * 3
,如果是,则无法满足条件,直接返回-1
。 - 使用哈希表
map
记录每个字符出现的次数。 - 遍历字符串,更新
map
中每个字符的计数。 - 检查
map
中的每个字符是否满足至少出现k
次的条件,如果不满足,则返回-1
。 - 使用双指针
left
和right
分别从字符串的两端开始遍历。 - 在每一步中,从右侧取一个字符,并更新
map
。 - 如果当前
map
中的字符计数不满足条件,则从左侧取一个字符,直到满足条件。 - 更新
max
变量,记录当前找到的最长连续满足条件的子串长度。 - 循环结束后,如果
max
仍然是-1
,则说明无法满足条件,返回-1
;否则,返回总长度减去max
的值,即为所需的最少分钟数。
代码实现
class Solution {
public int takeCharacters(String s, int k) {
int n = s.length();
int max = -1;
if (n < k * 3) return max; // 如果字符串长度小于k*3,则无法满足条件
HashMap<Character, Integer> map = new HashMap<>();
char[] chars = s.toCharArray();
// 遍历字符串,统计每个字符出现的次数
for (int right = 0; right < n; right++) {
map.put(chars[right], map.getOrDefault(chars[right], 0) + 1);
}
// 检查是否每个字符都至少出现k次
if (map.getOrDefault('a', 0) < k || map.getOrDefault('b', 0) < k || map.getOrDefault('c', 0) < k)
return max;
int left = 0;
int right = 0;
while (right < n) {
map.put(chars[right], map.getOrDefault(chars[right], 0) - 1); // 从右侧取一个字符
while (map.getOrDefault('a', 0) < k || map.getOrDefault('b', 0) < k || map.getOrDefault('c', 0) < k) {
map.put(chars[left], map.getOrDefault(chars[left], 0) + 1); // 从左侧取一个字符,直到满足条件
left++;
}
max = Math.max(max, right - left + 1); // 更新最长连续满足条件的子串长度
right++;
}
return max == -1 ? -1 : n - max; // 返回所需的最少分钟数
}
}