青训营X豆包MarsCode - 第三篇滑窗- 41| 豆包MarsCode AI 刷题
氦嗨氦,上两期已经速滑,有中等有困难,今日再挑一道巩固一下,让我们继续嗯滑,应滑尽滑!
今天这题其实也很经典,精髓就在于怎么看出来他是不是要用滑动窗口来开滑。
不说俏皮话了,题号 41 - 最小替换子串长度。
题目原文
小F得到了一个特殊的字符串,这个字符串只包含字符
A、S、D、F,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得A、S、D、F这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。
题目抽象分析
- “子串”
- 约束条件——替换操作使得字符串中的四个特定字符
A、S、D、F出现的频次相等 - 典型的滑动窗口问题
整体思路
为了找到最小的子串,
- 首先统计每个字符的当前频次,
- 然后确定每个字符需要增加或减少的频次以达到目标,
- then,我们使用滑动窗口的方法遍历字符串,调整窗口大小,不断比一比,until找到满足条件的最小子串,
return is fine
代码分块解析
优化一下,把滑窗以外和滑窗以内的部分split开,我们分开列举看看
全局变量部分
- 计算目标频次:
target_freq为字符串长度除以4,因为每个字符的目标频次应该是相等的。 - 统计当前频次:使用字典
current_freq来记录每个字符在字符串中出现的次数。 - 计算需要改变的频次:
need_change字典记录了每个字符需要增加或减少的次数。
滑动窗口以内
标准流程了
- 初始化窗口:
left和right分别表示窗口的左右边界,min_length记录满足条件的最小子串长度。 - 扩展窗口:通过内层循环,将字符添加到窗口中,并更新窗口内的字符频次。
- 检查并调整窗口:当窗口内的字符频次满足条件时,尝试缩小窗口以找到更小的子串。
解题步骤
- 感觉上面写的很通透了,这里就不展开写了。
代码部分
def solution(input):
n = len(input)
target_freq = n // 4 # 目标频次
# 统计当前频次
current_freq = {'A': 0, 'S': 0, 'D': 0, 'F': 0}
for char in input:
current_freq[char] += 1
# 计算每个字符需要增加或减少的次数
need_change = {char: max(0, current_freq[char] - target_freq) for char in 'ASDF'}
# 如果所有字符的频次已经相等,直接返回0
if sum(need_change.values()) == 0:
return 0
# 使用滑动窗口找到最小的子串
left = 0
min_length = n # 初始化为最大可能值
window_count = {'A': 0, 'S': 0, 'D': 0, 'F': 0}
for right in range(n):
window_count[input[right]] += 1
# 检查当前窗口是否满足替换条件
while all(window_count[char] >= need_change[char] for char in 'ASDF'):
min_length = min(min_length, right - left + 1)
window_count[input[left]] -= 1
left += 1
return min_length
if __name__ == "__main__":
# You can add more test cases here
print(solution("ADDF") == 1)
print(solution("ASAFASAFADDD") == 3)
复杂度分析
- 时间复杂度:O(n),其中 n 是字符串的长度。尽管有两层循环,但每个字符只被遍历一次。
- 空间复杂度:O(1),使用的额外空间与字符串长度无关,只存储了固定数量的字符频次。
总结
在实际这种刷题里面,滑动窗口技术常用于解决类似的最优子串问题,看到“子串”“子区间”这种连续seq就得要下意识反应了。
滑窗自此估计要告一段落,下个专题再考虑一下还做刷题or其他deep talk一点的,预告禁止(bushi
下次再say啦~