最小替换子串长度
问题描述
小F得到了一个特殊的字符串,这个字符串只包含字符A、S、D、F,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得A、S、D、F这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。
对题目的理解:
字符串的组成:
- 字符串只包含
A、S、D、F这四个字符。 - 字符串的长度总是4的倍数。
目标:
- 通过尽可能少的替换操作,使得字符串中
A、S、D、F这四个字符的出现频次相等。 - 替换操作是指将字符串中的某个字符替换为另一个字符。通过替换其中的字符,使得整个字符串中
A、S、D、F的频次相等。 - 替换操作是原地进行元素的更改,不需要后面的数组含有相同的元素(这种是交换)
测试样例
样例1:
输入:
input = "ADDF"
输出:1
样例2:
输入:
input = "ASAFASAFADDD"
输出:3
样例3:
输入:
input = "SSDDFFFFAAAS"
输出:1
示例解释
样例1:
输入:"ADDF"
输出:1
- 解释:字符串 "ADDF" 中,
A出现1次,S出现0次,D出现2次,F出现1次。 - 可以通过替换
D为S,得到 "ADSF",此时每个字符出现1次,频次相等。 - 替换的最小子串长度为1。
样例2:
输入:"ASAFASAFADDD"
输出:3
- 解释:字符串 "ASAFASAFADDD" 中,
A出现4次,S出现3次,D出现3次,F出现2次。 - 可以通过替换 "DDD" 为 "FFF",得到 "ASAFASAFFFF",此时每个字符出现4次,频次相等。
- 替换的最小子串长度为3。
样例3:
输入:"SSDDFFFFAAAS"
输出:1
- 解释:字符串 "SSDDFFFFAAAS" 中,
A出现3次,S出现3次,D出现2次,F出现4次。 - 可以通过替换
F为D,得到 "SSDDDDDDAAAS",此时每个字符出现4次,频次相等。 - 替换的最小子串长度为1。
通过对样例和题目的分析,我们可以选用滑动窗口这一方法解决这一题目。
解题思路:
变量初始化
len:存储字符串的长度。n:目标字符出现次数,每个字符应出现n = len / 4次才能平衡。s:将输入字符串input转换为字符数组,便于逐字符操作。left:窗口的左边界,初始为 0。ans:记录最小窗口长度,初始设为len。cnt:数组用于统计每个字符的频率。
统计字符频率
遍历字符数组 s,统计每个字符的出现次数,结果存储在 cnt 数组中。
初步检查平衡状态
- 如果字符 'A','S','D','F' 的频率均等于
n,即cnt['A'] == n && cnt['S'] == n && cnt['D'] == n && cnt['F'] == n,则字符串已经平衡,无需替换,直接返回 0。
滑动窗口寻找最小子串
-
遍历字符串的右边界
right:- 每次将右边界的字符
s[right]计数减 1,表示将该字符移入窗口。 - 然后检查窗口内部的字符分布:只要
cnt['A'] <= n && cnt['S'] <= n && cnt['D'] <= n && cnt['F'] <= n成立,说明当前窗口可以让剩余部分平衡。 - 在满足条件的情况下,更新
ans为当前的最小窗口长度,即ans = Math.min(ans, right - left + 1)。 - 随后移动
left向右以尝试缩小窗口,并将cnt[s[left]]加 1(恢复移除的字符计数)。
- 每次将右边界的字符
代码如下所示:
public static int solution(String input) {
int len = input.length(); // 字符串长度
int n = len / 4; // 每个字符应出现的频率
char[] s = input.toCharArray(); // 将字符串转换为字符数组
int left = 0; // 窗口左边界
int ans = len; // 初始化最小窗口长度为整个字符串的长度
int[] cnt = new int['X']; // 计数数组
// 统计每个字符出现的次数
for (char i : s) {
cnt[i]++;
}
// 检查是否已经平衡
if (cnt['A'] == n && cnt['S'] == n && cnt['D'] == n && cnt['F'] == n) {
return 0; // 已经平衡,无需替换
}
// 滑动窗口
for (int right = 0; right < len; right++) {
cnt[s[right]]--; // 右边界字符移入窗口,减少它的计数
// 检查当前窗口是否满足平衡条件
while (cnt['A'] <= n && cnt['S'] <= n && cnt['D'] <= n && cnt['F'] <= n) {
ans = Math.min(ans, right - left + 1); // 更新最小子串长度
cnt[s[left++]]++; // 移动左边界,恢复移出窗口的字符计数
}
}
return ans; // 返回结果
}
总结
本题通过滑动窗口快速调整并判断子串长度。使用计数数组追踪字符出现频率,确保窗口内满足条件时更新结果。要能准确识别出子数组和最值所采用的方法是什么。MarsCode AI在这一过程中提供了许多的帮助。
在做这一题的过程中,MarsCode AI工具会提供解题提示(如思路提示,代码提示,检查代码等),并在解题后立即给出分析,包括解题思路的优化建议。在遇到不懂的地方还可以通过反复提问来理清自己的思路。还能生成详细的解题步骤和相关知识点的讲解。我们能有效避免重复犯错,并掌握相似题型的解题方法。MarsCode AI在学习中的独特价值在于,它们能帮助用户快速定位核心知识点,减少前期因不熟练而带来的不必要的思考。
同时,利用MarsCode AI,我们可以在做题思路不畅的情况下,通过思路提示,降低当前的思考难度,在明确的思路下进行代码的编写。还可以通过代码提示,结合已学过的知识,进行这一题目代码框架的搭建,通过完善关键步骤来完成这道题目。使用AI工具检查代码,可以快速定位代码中的问题,比如数组越界等。并通过这一报错来思考解决办法。总之,通过使用MarsCode AI工具,刷题的效率和质量都有了大幅的提升。