最小替换子串长度-MarsCode AI 刷题

46 阅读5分钟

问题描述

小F得到了一个特殊的字符串,这个字符串只包含字符ASDF,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得ASDF这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。


测试样例

样例1:

输入:input = "ADDF"
输出:1

样例2:

输入:input = "ASAFASAFADDD"
输出:3

样例3:

输入:input = "SSDDFFFFAAAS"
输出:1

样例4:

输入:input = "AAAASSSSDDDDFFFF"
输出:0

样例5:

输入:input = "AAAADDDDAAAASSSS"
输出:4

    n = len(input)
    target = n // 4
    count = {'A': 0, 'S': 0, 'D': 0, 'F': 0}
    
    # 统计原始字符串中的字符频次
    for char in input:
        count[char] += 1
    
    # 如果字符频次已经相等,返回0
    if count['A'] == count['S'] == count['D'] == count['F'] == target:
        return 0
    
    # 定义滑动窗口的左右边界
    left = 0
    min_len = n  # 初始化最小长度为字符串的长度
    
    # 滑动窗口
    for right in range(n):
        count[input[right]] -= 1  # 移动右边界,更新频次
        
        # 当窗口内的频次可以通过替换让它们接近目标时
        while (count['A'] <= target and count['S'] <= target and 
               count['D'] <= target and count['F'] <= target):
            # 更新最小窗口长度
            min_len = min(min_len, right - left + 1)
            count[input[left]] += 1  # 收缩左边界,更新频次
            left += 1
            
    return min_len

# 测试样例
if __name__ == "__main__":
    print(solution("ADDF") == 1)
    print(solution("ASAFASAFADDD") == 3)
    print(solution("SSDDFFFFAAAS") == 1)
    print(solution("AAAASSSSDDDDFFFF") == 0)
    print(solution("AAAADDDDAAAASSSS") == 4)

这个代码实现的是一个通过滑动窗口算法求解最小子串长度的问题,目标是使字符串中字符 'A', 'S', 'D', 'F' 的频次均接近目标频次。具体来说,目标是将这些字符的频次调整为字符串长度的四分之一(即 n // 4),然后通过滑动窗口找到最小长度的子串,使得这个子串内包含至少一个字符 'A', 'S', 'D', 'F' 的频次达到目标频次。

代码解析

1. 输入及目标频次

pythonCopy Code
n = len(input)
target = n // 4
  • n 是输入字符串的长度。
  • target 是字符 'A''S''D''F' 的目标频次,每个字符的频次应该接近 n // 4,即将输入字符串均匀分配给这四个字符。

2. 统计字符频次

pythonCopy Code
count = {'A': 0, 'S': 0, 'D': 0, 'F': 0}
for char in input:
    count[char] += 1
  • 使用字典 count 来记录字符 'A''S''D''F' 在输入字符串中的出现频次。
  • 对输入字符串进行遍历,并更新对应字符的频次。

3. 处理已满足目标频次的情况

pythonCopy Code
if count['A'] == count['S'] == count['D'] == count['F'] == target:
    return 0
  • 如果每个字符的频次已经等于目标频次,直接返回 0,因为不需要任何调整。

4. 滑动窗口

pythonCopy Code
left = 0
min_len = n
for right in range(n):
    count[input[right]] -= 1  # 移动右边界,更新频次
    
    while (count['A'] <= target and count['S'] <= target and
           count['D'] <= target and count['F'] <= target):
        min_len = min(min_len, right - left + 1)
        count[input[left]] += 1  # 收缩左边界,更新频次
        left += 1
  • left 是窗口的左边界,right 是窗口的右边界,遍历 right 从 0 到 n-1
  • 在每一步,right 对应的字符频次会减少,表示滑动窗口扩展。
  • 内层 while 循环会检查当前窗口内各字符的频次是否都小于或等于目标频次。若满足条件,更新 min_len(当前最小子串长度),然后收缩左边界,继续优化窗口。

5. 返回结果

pythonCopy Code
return min_len
  • 最后返回 min_len,即满足条件的最小子串长度。

代码缺陷与改进

1. 目标频次的定义与判断

pythonCopy Code
while (count['A'] <= target and count['S'] <= target and
       count['D'] <= target and count['F'] <= target):
  • 这个判断条件有误,因为我们希望窗口中某些字符的频次可以大于目标频次,而不是都必须小于等于目标频次。更准确的判断应该是检查是否当前窗口的某些字符的频次大于目标频次,需要通过替换让它们接近目标频次。

2. 频次更新逻辑

  • count[input[right]] -= 1 和 count[input[left]] += 1 的更新方式是错误的。滑动窗口的右边界是扩展窗口,左边界是收缩窗口,所以当增加右边界字符时应该增加频次,而当收缩左边界时应该减少对应字符的频次。

3. 变量命名和理解

  • 代码中使用了 count 字典来记录字符频次,但是 target 代表的是字符的目标频次,而 count 只是用于计算实际频次,改进时可以考虑让代码更具可读性和逻辑性。

改进后的思路

  1. 使用滑动窗口算法来动态调整窗口内的字符频次。
  2. 通过在窗口内增加或者减少字符频次来逼近目标频次。
  3. 在窗口满足要求时,更新最小窗口长度,最终返回该最小值。

改进后的滑动窗口实现可以如下:

pythonCopy Code
def solution(input):
    n = len(input)
    target = n // 4
    count = {'A': 0, 'S': 0, 'D': 0, 'F': 0}
    
    # 统计原始字符串中的字符频次
    for char in input:
        count[char] += 1
    
    # 如果字符频次已经相等,返回0
    if count['A'] == count['S'] == count['D'] == count['F'] == target:
        return 0
    
    # 定义滑动窗口的左右边界
    left = 0
    min_len = n  # 初始化最小长度为字符串的长度
    
    # 滑动窗口
    for right in range(n):
        count[input[right]] -= 1  # 移动右边界,更新频次
        
        # 当窗口内的频次可以通过替换让它们接近目标时
        while all(count[char] <= target for char in 'ASDF'):
            min_len = min(min_len, right - left + 1)
            count[input[left]] += 1  # 收缩左边界,更新频次
            left += 1
            
    return min_len

这样做可以让算法更加符合题意,通过滑动窗口来有效地求解最小子串长度问题。