问题描述
小F得到了一个特殊的字符串,这个字符串只包含字符A、S、D、F,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得A、S、D、F这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。
测试样例
样例1:
输入:
input = "ADDF"
输出:1
样例2:
输入:
input = "ASAFASAFADDD"
输出:3
思路解析
这个问题要求我们找到一个最小的连续子串,通过替换其中的一些字符,使得字符串中A、S、D、F四个字符的出现次数相等。由于字符串长度是4的倍数,我们的目标是让每个字符出现的次数也相等。
解决这个问题的关键在于使用滑动窗口的方法。以下是解决这个问题的步骤:
- 统计字符频次:首先,我们统计字符串中每个字符出现的次数。
- 计算超出目标频次的字符:然后,我们计算每个字符超出目标频次(字符串长度的四分之一)的次数,如果某个字符的频次不足目标频次,则记为0。
- 初始化窗口计数器:接着,我们初始化一个窗口计数器
window_count来跟踪当前窗口内各字符的出现次数,并设置左右指针left和right,以及最小长度min_len为整个字符串的长度。 - 滑动窗口:我们从左到右遍历字符串,将当前字符加入窗口计数器中。
- 检查窗口条件:如果窗口内的字符计数满足所有字符都至少达到了需要移除的最小数量,即
all(window_count[key] >= excess[key] for key in excess),则尝试更新最小长度min_len,并移动左指针,减少窗口大小。
通过这种方法,我们可以找到最小的连续子串,通过替换其中的字符,使得每个字符的出现次数相等。
代码
from collections import Counter # 导入Counter类,用于统计元素出现的次数
def solution(input): # 定义函数solution,接受一个字符串作为输入
count = Counter(input) # 使用Counter统计输入字符串中每个字符出现的次数
target = len(input) // 4 # 计算目标数量,即字符串长度的四分之一
excess = {key: max(0, value - target) for key, value in count.items()} # 计算每种字符超出目标数量的多余次数
need = sum(excess.values()) # 计算总共需要移除的字符数量
if need == 0: # 如果没有多余的字符,直接返回0
return 0
min_len = len(input) # 初始化最小长度为输入字符串的全长
left = 0 # 初始化左指针
window_count = Counter() # 初始化窗口内的字符计数器
for right in range(len(input)): # 遍历字符串,使用右指针
window_count[input[right]] += 1 # 将当前字符加入窗口计数器
while all(window_count[key] >= excess[key] for key in excess): # 如果窗口内的字符计数都达到了需要移除的最小数量
min_len = min(min_len, right - left + 1) # 更新最小长度
window_count[input[left]] -= 1 # 将左指针的字符从窗口计数器中移除
left += 1 # 移动左指针
return min_len # 返回计算出的最小长度
复杂度分析
时间复杂度分析
-
初始化计数器
count:使用Counter(input)对输入字符串进行计数,时间复杂度为 O(n),其中 n 是字符串的长度。 -
计算
excess和need:遍历count字典,计算每个字符的excess值,时间复杂度为 O(1),因为字符种类固定为 4 种。计算need的时间复杂度也是 O(1)。 -
滑动窗口部分: 外层循环遍历整个字符串,时间复杂度为 O(n)。内层
while循环在最坏情况下会遍历整个字符串,但由于每次left指针移动时,窗口内的字符数会减少,因此总体时间复杂度仍然是 O(n)。 整个算法的时间复杂度为 O(n)。
空间复杂度分析
-
计数器
count和window_count:count和window_count都是字典,最多存储 4 种字符的计数,因此空间复杂度为 O(1)。 -
其他变量:
excess和need的空间复杂度也是 O(1)。
整个算法的空间复杂度为 O(1)。