问题描述
小F得到了一个特殊的字符串,这个字符串只包含字符A、S、D、F,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得A、S、D、F这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。
测试样例
样例1:
输入:
input = "ADDF"
输出:1
样例2:
输入:
input = "ASAFASAFADDD"
输出:3
样例3:
输入:
input = "SSDDFFFFAAAS"
输出:1
样例4:
输入:
input = "AAAASSSSDDDDFFFF"
输出:0
样例5:
输入:
input = "AAAADDDDAAAASSSS"
输出:4
解题思路
-
字符串的长度特性:
- 字符串的长度
n总是 4 的倍数。因为最终需要将每个字符的出现次数调整为相等,那么每个字符最终应该出现的次数是n / 4。
- 字符串的长度
-
计算目标频次:
- 设目标频次
target = n / 4,即每个字符应该出现的次数。
- 设目标频次
-
统计字符频次:
- 我们可以统计字符串中每个字符的出现次数,并计算当前各个字符的差异(即当前频次与目标频次的差值)。如果某个字符的出现次数大于目标频次,则多出来的部分可以用于替换;如果某个字符的出现次数小于目标频次,则需要替换其他字符来增加该字符的频次。
-
使用滑动窗口(滑动区间):
- 我们的目标是找出一个最小的子串,使得替换后,四个字符的频次能够相等。由于我们需要考虑替换操作的最小子串长度,可以使用滑动窗口来高效求解。
- 滑动窗口的做法是从字符串的左侧开始遍历,同时调整窗口的大小,保持窗口内的字符频次尽量接近目标频次。当窗口内的字符频次接近目标频次时,我们尝试缩小窗口,最终得到最小的窗口长度。
-
具体步骤:
- 统计整个字符串的字符频次。
- 计算每个字符的多余部分(即大于目标频次的部分)和缺失部分(即小于目标频次的部分)。
- 使用滑动窗口(双指针)方法,尽可能将窗口内的字符频次调整为目标频次。
代码解释
-
初始化与统计频次:
target = n // 4表示每个字符应该出现的次数。- 使用
Counter(input)来统计字符串中每个字符的出现次数。
-
滑动窗口:
left和right是滑动窗口的左右边界,right逐步向右移动,每次扩展窗口。window_count用于统计当前窗口内各字符的频次。
-
窗口调整:
- 对每一个扩展后的窗口,我们检查其内的字符频次是否都不超过目标频次
target。如果是,说明当前窗口内的字符频次已经平衡,可以缩小窗口,移动left指针来尽量减小子串长度。
- 对每一个扩展后的窗口,我们检查其内的字符频次是否都不超过目标频次
-
最小子串长度:
- 在每次调整窗口时,我们记录窗口的最小长度
min_length,即最小的替换操作所需子串长度。
- 在每次调整窗口时,我们记录窗口的最小长度
测试样例分析
样例 1
输入:"ADDF"
- 目标频次:每个字符的目标频次为
4 // 4 = 1。 - 当前频次:
A: 1, D: 2, F: 1,需要替换一个D为A或F。 - 最小替换子串长度是 1,因为只需要一个字符替换。
样例 2
输入:"ASAFASAFADDD"
- 目标频次:每个字符的目标频次为
12 // 4 = 3。 - 当前频次:
A: 4, S: 3, F: 3, D: 2,需要将一个A替换为D,所以最小替换子串长度是 3。
样例 3
输入:"SSDDFFFFAAAS"
- 目标频次:每个字符的目标频次为
12 // 4 = 3。 - 当前频次:
S: 3, D: 3, F: 4, A: 2,需要替换一个F为A,最小替换子串长度是 1。
样例 4
输入:"AAAASSSSDDDDFFFF"
- 目标频次:每个字符的目标频次为
16 // 4 = 4。 - 当前频次已经满足目标频次,所以无需替换,输出为 0。
样例 5
输入:"AAAADDDDAAAASSSS"
- 目标频次:每个字符的目标频次为
16 // 4 = 4。 - 当前频次:
A: 6, D: 3, S: 4,需要替换两个A和一个D,最小替换子串长度为 4。
总结
- 滑动窗口 是处理这类子串问题的高效方法,通过动态调整窗口大小来达到最小化替换的目标。
- 时间复杂度:每个字符的频次最多更新一次,滑动窗口的左右指针遍历整个字符串,因此时间复杂度为
O(n),其中n是字符串的长度。 - 空间复杂度:
O(1),只使用了固定数量的额外空间来存储窗口内的字符频次。