如何通过最小替换实现字符串平衡:问题分析与解决方案
在这篇笔记中,我们深入探讨一个与字符串操作相关的问题:给定一个只包含字符 A、S、D、F 的字符串,其长度是 4 的倍数,如何通过最少的字符替换,使得这四种字符的频次相等。本文从问题分析到解决方法,将分步讲解,并附上完整的代码与测试样例,帮助你彻底掌握该问题。
问题描述
给定一个字符串 input,它仅包含 A、S、D、F,且长度总是 4 的倍数。我们的任务是通过尽可能少的替换,使得 A、S、D、F 在字符串中出现的频次完全相等。
例如:
- 输入
"ADDF",目标是将A、D、F的频次都调整为1。 - 输入
"ASAFASAFADDD",目标是将频次调整为3。
解决思路
要解决这个问题,我们需要以下关键步骤:
1. 计算目标频次
由于字符串的长度始终是 4 的倍数,因此我们可以将总长度除以 4,得到每个字符的目标频次。
例如:
- 对于长度为
12的字符串,每个字符的目标频次是3。 - 对于长度为
16的字符串,每个字符的目标频次是4。
2. 初始频次统计
统计字符串中每个字符当前的出现次数,与目标频次进行比较。如果所有字符的当前频次都已经小于或等于目标频次,则不需要替换,直接返回 0。
3. 滑动窗口优化
如果初始状态未达到目标平衡,可以使用滑动窗口方法在字符串中查找需要替换的最小子串:
- 初始化两个指针
left和right,用来表示当前窗口的范围。 - 不断扩大
right指针,将窗口范围扩大。 - 在每次扩大窗口后,判断当前窗口能否覆盖所有需要减少的字符。如果能覆盖,则记录当前窗口长度,并尝试收缩窗口(移动
left指针),以找到更小的满足条件的子串。
4. 最小化子串长度
通过滑动窗口的双指针操作,动态调整窗口的范围,确保最终记录的子串长度最小。
实现代码
以下是完整的 Python 实现代码,充分利用了滑动窗口算法来解决问题:
python
from collections import Counter
def solution(input):
n = len(input)
target_count = n // 4 # 每个字符的目标频次
count = Counter(input) # 当前字符频次统计
min_len = n # 初始化最小子串长度为整个字符串
# 检查是否已经满足条件
def is_balanced(count):
return all(count[char] <= target_count for char in "ASDF")
# 如果已经平衡,不需要替换
if is_balanced(count):
return 0
# 滑动窗口
left = 0
for right in range(n):
count[input[right]] -= 1 # 窗口右边界字符频次减少
# 检查窗口内是否满足条件
while is_balanced(count):
min_len = min(min_len, right - left + 1)
count[input[left]] += 1 # 收缩左边界
left += 1
return min_len
测试样例与分析
以下是多个测试样例的输入与输出分析。
样例 1
- 输入:
input = "ADDF" - 目标频次:每个字符出现
1次。 - 初始频率统计:
{'A': 1, 'D': 2, 'F': 1}。 - 滑动窗口找到的最小子串是
"DD",只需替换其中一个字符即可平衡。
输出:1
样例 2
-
输入:
input = "ASAFASAFADDD" -
目标频次:每个字符出现
3次。 -
初始频率统计
:
{'A': 5, 'S': 3, 'F': 3, 'D': 1}。
A多 2,D少 2。
-
滑动窗口找到的最小覆盖子串是
"DDD",替换后即可平衡。
输出:3
样例 3
-
输入:
input = "SSDDFFFFAAAS" -
目标频次:每个字符出现
3次。 -
初始频率统计
:
{'S': 4, 'D': 2, 'F': 4, 'A': 2}。
S多 1,D少 1。
-
滑动窗口找到的最小子串是
"SS"。
输出:1
样例 4
- 输入:
input = "AAAASSSSDDDDFFFF" - 目标频次:每个字符出现
4次。 - 初始频率统计:
{'A': 4, 'S': 4, 'D': 4, 'F': 4}。 - 已经满足目标频次,无需替换。
输出:0
样例 5
-
输入:
input = "AAAADDDDAAAASSSS" -
目标频次:每个字符出现
4次。 -
初始频率统计
:
{'A': 8, 'D': 4, 'S': 4, 'F': 0}。
A多 4,F少 4。
-
滑动窗口找到的最小覆盖子串是
"AAAA"。
输出:4
时间与空间复杂度分析
时间复杂度
- 初始频次统计:
O(n)。 - 滑动窗口遍历:
O(n)。
总时间复杂度为 O(n),非常高效。
空间复杂度
- 使用了一个字典(
Counter)来统计频率,空间复杂度为O(1)(仅存储4个字符的频次)。
总结
- 本问题是滑动窗口算法的经典应用之一,通过双指针动态调整窗口范围,可以高效地解决最小子串问题。
- 核心思想是将大问题分解为子问题:逐步缩小需要替换的子串范围,同时保证不影响全局平衡。
- 本文实现的解决方案不仅代码简洁,而且具备良好的时间复杂度,适合处理大规模输入。
通过本文的学习,相信你对滑动窗口算法以及字符串操作的优化有了更深刻的理解!