最小替换子串长度 | 豆包MarsCode AI刷题

5 阅读5分钟

解题思路

  1. 理解问题:这个问题要求我们通过最少的替换操作,使得字符串中的字符'A'、'S'、'D'、'F'出现的次数相等,且这个次数是字符串长度除以4的结果。
  2. 初始化:首先,我们计算出每个字符应该出现的目标次数(target_freq),然后初始化一个字典(char_freq)来记录每个字符在字符串中的出现次数。
  3. 统计字符频率:遍历字符串,统计每个字符的出现次数。
  4. 检查初始状态:如果初始状态下,每个字符的出现次数已经等于目标次数,那么不需要任何替换,返回0。
  5. 滑动窗口:使用滑动窗口的方法来找到需要替换的最小子串长度。滑动窗口是一种在字符串问题中常用的技术,用于在不改变字符串其他部分的情况下,检查字符串的某个连续子串。
  6. 更新窗口:在滑动窗口的过程中,我们不断尝试缩小窗口,直到窗口内所有字符的出现次数都不大于目标次数。
  7. 记录最小长度:在滑动窗口的过程中,记录并更新需要替换的最小子串长度。
  8. 返回结果:最后,返回找到的最小长度。如果没有找到合适的子串长度,则返回0。

代码实现

def solution(input_str):
    n = len(input_str)
    target = n // 4  # 因为是4的倍数,所以每个字符应该出现n/4次
    
    # 统计每个字符出现的次数
    freq = {'A': 0, 'S': 0, 'D': 0, 'F': 0}
    for c in input_str:
        freq[c] += 1
        
    # 如果已经平衡,返回0
    if all(f == target for f in freq.values()):
        return 0
        
    # 从小到大尝试子串长度
    for length in range(1, n + 1):
        # 遍历所有可能的起始位置
        for start in range(n - length + 1):
            # 尝试替换这个子串后的频次
            new_freq = freq.copy()
            # 减去要替换的子串中的字符频次
            for i in range(start, start + length):
                new_freq[input_str[i]] -= 1
                
            # 检查是否可以通过替换这个子串达到平衡
            needed = {'A': 0, 'S': 0, 'D': 0, 'F': 0}
            valid = True
            for char in new_freq:
                if new_freq[char] > target:  # 如果某个字符频次超过目标值,无法通过替换达到平衡
                    valid = False
                    break
                needed[char] = target - new_freq[char]
                
            if valid and sum(needed.values()) == length:
                return length
                
    return -1

if __name__ == "__main__":
    #  You can add more test cases here
    print(solution("ADDF") == 1 )
    print(solution("ASAFASAFADDD") == 3)

代码详解

函数定义及初始化部分
def solution(input_str):
    n = len(input_str)
    target = n // 4  # 因为是4的倍数,所以每个字符应该出现n/4次

    # 统计每个字符出现的次数
    freq = {'A': 0, 'S': 0, 'D': 0, 'F': 0}
    for c in input_str:
        freq[c] += 1
  • 首先,通过 len(input_str) 获取输入字符串的长度 n,并计算出每个字符理想的出现频次 target,即字符串长度除以 4。
  • 然后,创建了一个字典 freq,用于统计输入字符串 input_str 中每个字符('A''S''D''F')出现的次数。通过遍历输入字符串,对每个字符在 freq 字典中的计数进行累加。
检查初始状态是否已经平衡
if all(f == target for f in freq.values()):
    return 0
  • 这里使用了 all() 函数来检查 freq 字典中每个字符的出现频次是否都已经等于之前计算出的理想频次 target。如果是,说明字符串已经处于平衡状态,直接返回 0,表示不需要进行任何替换操作。
尝试不同子串进行替换的循环部分
# 从小到大尝试子串长度
for length in range(1, n + 1):
    # 遍历所有可能的起始位置
    for start in range(n - length + 1):
        # 尝试替换这个子串后的频次
        new_freq = freq.copy()
        # 减去要替换的子串中的字符频次
        for i in range(start, start + length):
            new_freq[input_str[i]] -= 1

        # 检查是否可以通过替换这个子串达到平衡
        needed = {'A': 0, 'S': 0, 'D': 0, 'F': 0}
        valid = True
        for char in new_freq:
            if new_freq[char] > target:  # 如果某个字符频次超过目标值,无法通过替换达到平衡
                valid = False
                break
            needed[char] = target - new_freq[char]

        if valid and sum(needed.values()) == length:
            return length
  • 外层循环 for length in range(1, n + 1):从长度为 1 开始,逐步增加子串的长度,直到字符串的长度 n,尝试所有可能的子串长度。
  • 中层循环 for start in range(n - length + 1):对于每个确定的子串长度 length,遍历字符串中所有可能的起始位置,以便考虑所有可能的子串。
  • 在内层循环之前,首先通过 new_freq = freq.copy() 创建了一个 freq 字典的副本 new_freq,用于模拟替换子串后的字符频次情况。然后,通过内层循环 for i in range(start, start + length),减去要替换的子串中的每个字符在 new_freq 中的频次。
  • 接下来,再次遍历 new_freq 字典中的每个字符,检查替换子串后的字符频次情况。如果某个字符的频次超过了目标频次 target,则说明无法通过替换当前子串达到平衡状态,将 valid 标记设为 False 并跳出循环。否则,计算出为了达到平衡每个字符还需要出现的次数,存储在 needed 字典中。
  • 最后,如果 valid 为 True 且 needed 字典中所有值的总和等于当前尝试的子串长度 length,这意味着可以通过替换当前子串达到平衡状态,此时返回这个子串的长度 length
返回未找到合适子串的情况
return -1
  • 如果在遍历了所有可能的子串长度和起始位置后,都没有找到能够通过替换达到平衡状态的子串,那么函数最终返回 -1,表示无法实现字符串的平衡。
测试
if __name__ == "__main__":
    #  You can add more test cases here
    print(solution("ADDF") == 1 )
    print(solution("ASAFASAFADDD") == 3)
  • 在 if __name__ == "__main__" 语句块中,对 solution 函数进行了简单的测试。通过调用 solution 函数并传入不同的测试字符串,然后将函数返回值与预期结果进行比较,并打印出比较结果(这里是判断返回值是否等于预期的结果值,返回 True 或 False)。可以根据需要在这里添加更多的测试用例来进一步验证函数的正确性。