替换最小子串长度问题刷题笔记
一、题目解析
问题描述
给定一个仅包含字符 A、S、D、F 的字符串,其长度总是 444 的倍数。我们需要通过替换最少的字符,使得每种字符的出现频次相等(即每种字符的频次均为length/4)。求实现这一目标所需替换的最小子串长度。
输入输出说明
- 输入:一个由
A、S、D、F组成的字符串。 - 输出:实现条件的最小子串长度。
测试样例
- 样例1:输入
"ADDF",输出1。 - 样例2:输入
"ASAFASAFADDD",输出3。 - 样例3:输入
"AAAASSSSDDDDFFFF",输出0。
二、解题思路
-
目标频次计算
每种字符的目标频次为字符串长度 n 除以 4,即 target=n/4。 -
窗口滑动法
- 使用一个滑动窗口来表示当前需要替换的子串。
- 初始时统计所有字符的频次,判断是否已满足条件。
- 不满足时,尝试右移窗口,缩减子串长度,直到满足条件或滑动到末尾。
-
条件判断
- 在滑动窗口中,每次移动右边界,统计窗口外字符的频次是否仍满足 ≤target。
- 如果满足条件,尝试移动左边界,进一步缩小窗口长度。
-
复杂度分析
- 遍历字符串时,每个字符最多进入一次窗口,离开一次窗口,时间复杂度为 O(n)。
- 字符频次统计操作为常数时间,整体复杂度为 O(n)。
三、代码实现
代码以及注释:
import java.util.HashMap;
import java.util.Map;
public class Main {
public static int solution(String input) {
int n = input.length(); // 字符串长度
int target = n / 4; // 每个字符的目标频次
Map<Character, Integer> count = new HashMap<>(); // 统计字符频次
count.put('A', 0);
count.put('S', 0);
count.put('D', 0);
count.put('F', 0);
// 初始化每种字符的频次
for (char c : input.toCharArray()) {
count.put(c, count.get(c) + 1);
}
// 如果所有字符的频次都满足目标,直接返回 0
if (count.get('A') == target && count.get('S') == target &&
count.get('D') == target && count.get('F') == target) {
return 0;
}
int minLength = n; // 最小子串长度初始化为字符串长度
int left = 0; // 滑动窗口的左边界
// 滑动窗口右边界
for (int right = 0; right < n; right++) {
char rightChar = input.charAt(right);
// 将右边界的字符移出窗口,更新频次
count.put(rightChar, count.get(rightChar) - 1);
// 判断当前窗口外的字符频次是否满足条件
while (count.get('A') <= target && count.get('S') <= target &&
count.get('D') <= target && count.get('F') <= target) {
// 更新最小子串长度
minLength = Math.min(minLength, right - left + 1);
// 移动左边界,尝试缩小窗口
char leftChar = input.charAt(left);
count.put(leftChar, count.get(leftChar) + 1); // 恢复移出窗口的字符频次
left++; // 左边界右移
}
}
return minLength; // 返回最小子串长度
}
public static void main(String[] args) {
// 测试样例
System.out.println(solution("ADDF") == 1); // 示例 1
System.out.println(solution("ASAFASAFADDD") == 3); // 示例 2
System.out.println(solution("SSDDFFFFAAAS") == 1); // 示例 3
System.out.println(solution("AAAASSSSDDDDFFFF") == 0); // 示例 4
System.out.println(solution("AAAADDDDAAAASSSS") == 4); // 示例 5
}
}
四、知识总结
-
滑动窗口技巧
- 滑动窗口是一种高效解决子串问题的算法,适合连续区间处理场景。
- 核心是动态维护窗口的条件满足状态,尝试缩小窗口以找到最优解。
-
字符频次统计
HashMap是统计字符频次的高效工具,插入和查找操作的时间复杂度为 O(1)。
-
边界条件处理
- 初始字符频次满足目标时直接返回 0。
- 滑动窗口中的每次条件判断需要确保所有字符均满足频次要求。
五、学习建议
-
滑动窗口的熟练掌握
- 对于子串长度优化问题,滑动窗口是首选解法。需要熟悉窗口左、右边界的动态调整逻辑。
-
多场景测试
-
测试时需覆盖以下情况:
- 字符频次均已满足的输入(返回 0)。
- 频次严重不平衡的输入(需要较大替换)。
- 特殊字符排列(如连续一个字符)。
-
-
优化代码逻辑
- 滑动窗口的核心是维护条件的动态性,需避免频繁计算或重复判断。
六、个人总结
本题通过滑动窗口和频次统计的结合,高效解决了替换最小子串长度的问题。在实际场景中,类似字符分布优化的问题广泛应用于文本处理、数据平衡等领域。这次刷题增强了对滑动窗口技巧的理解,同时也体会到条件约束的动态维护在算法设计中的重要性。