leetcode 424 - 替换后的最长重复字符

题目

给你一个字符串 s 和一个整数 k 。你可以选择字符串中的任一字符,并将其更改为任何其他大写英文字符。该操作最多可执行 k 次。
在执行上述操作后,返回 包含相同字母的最长子字符串的长度。

示例 1:
输入:s = "ABAB", k = 2
输出:4
解释:用两个'A'替换为两个'B',反之亦然。

示例 2:
输入:s = "AABABBA", k = 1
输出:4
解释:
将中间的一个'A'替换为'B',字符串变为 "AABBBBA"。
子串 "BBBB" 有最长重复字母, 答案为 4。
可能存在其他的方法来得到同样的结果。

提示:

  • 1 <= s.length <= 105
  • s 仅由大写英文字母组成
  • 0 <= k <= s.length

分析题目

  1. 求最长子串长度,符合动态滑动窗口解法特征;
  2. 在获取给定字符串长度 nn 基础上,先进行边界处理,如 n 为 1 或 n-1 小于修改次数 k 的情况(说明 n 长度内都能被修改次数覆盖);
  3. 定义一个哈希集,用于保存字符重复次数;
  4. 开始滑动窗口(移动右边界),每次滑动都更新右边界当前字符的哈希集计数,并检查当前窗口的有效性:
    • 如果当前窗口长度减去最大字符次数后,大于可修改次数,表示可能存在其他字母无法被修改,则触发窗口左边界收缩;
    • 如果不大于可修改次数,说明仍符合有效窗口范围,则更新最大有效窗口长度,并继续移动右边界;
  5. 左边界收缩:
    • 方式 1:每次收缩只移动一位,因为右边界已经先移动的一位导致触发条件收缩,所以正常左边界只需移动一位就能恢复到窗口有效长度范围,而不管当前窗口是否为有效窗口(因为窗口最大长度是取当前和历史窗口中的最大值);
    • 方式 2:每次收缩时只移动一位,并同步更新最大字符次数,此时的最大字符次数表示的是当前窗口中最大字符次数,并以此为基础作为下一轮的窗口有效性判断条件;
  6. 检查当前移动后的有效窗口长度,如果大于最大有效窗口长度,则更新最大有效窗口长度;
  7. 返回最终的最大有效窗口长度。

go 实现示例

  • 解法 1
func characterReplacement(s string, k int) int {

	// 获取给定字符串s长度
	n := len(s)

	// 如果s长度为1,或修改次数k大于s长度,则直接返回s长度
	if k >= n-1 || n == 1 {
		return n
	}

	// 定义字符统计哈希集
	charCount := make(map[byte]int)

	// 定义滑动窗口左边界
	left := 0

	// 定义最大字符出现次数存储
	maxCount := 0

	// 定义最大有效窗口长度存储
	maxLength := 0

	// 开始滑动窗口
	for right := 0; right < n; right++ {

		// 更新右边界当前字符的哈希集计数
		charCount[s[right]]++

		// 如果当前字符计数超过最大字符次数,则更新最大字符次数
		if charCount[s[right]] > maxCount {
			maxCount = charCount[s[right]]
		}

		// 检查当前窗口有效性,如果当前窗口长度减去最大字符次数后,大于可修改次数,表示可能存在其他字母无法被修改,无法满足最长相同字母子串的要求,则触发窗口左边界收缩
		if right-left+1-maxCount > k {
			// 先减少左边界当前字符的哈希集计数,如果计数归0,则删除该键,然后左边界向右移动一位
			// 收缩时只移动一位左边界,是因为右边界已经先移动了一位,窗口有效长度超出才触发了条件收缩,所以同理左边界只需移动一位又会回到窗口有效长度范围,即满足窗口长度,但不一定当前窗口就是有效窗口
			charCount[s[left]]--
			if charCount[s[left]] == 0 {
				delete(charCount, s[left])
			}
			left++
		}

		// 重新计数当前窗口有效长度,如果大于最大有效窗口长度,则更新最大有效窗口长度
		currLength := right - left + 1
		if currLength > maxLength {
			maxLength = currLength
		}

	}

	// 返回最终的有效窗口长度
	return maxLength
}
  • 解法 2
func characterReplacement(s string, k int) int {

	// 获取给定字符串s长度
	n := len(s)

	// 如果s长度为1,或修改次数k大于s长度,则直接返回s长度
	if k >= n-1 || n == 1 {
		return n
	}

	// 定义字符统计哈希集
	charCount := make(map[byte]int)

	// 定义滑动窗口左边界
	left := 0

	// 定义最大字符出现次数存储
	maxCount := 0

	// 定义最大有效窗口长度存储
	maxLength := 0

	// 开始滑动窗口
	for right := 0; right < n; right++ {

		// 更新右边界当前字符的哈希集计数
		charCount[s[right]]++

		// 获取当前窗口中最大字符计数
		for _, count := range charCount {
			if count > maxCount {
				maxCount = count
			}
		}

		// 检查当前窗口有效性,如果当前窗口长度减去最大字符次数后,大于可修改次数,表示可能存在其他字母无法被修改,无法满足最长相同字母子串的要求,则触发窗口左边界收缩
		if right-left+1-maxCount > k {

			// 先减少左边界当前字符的哈希集计数,如果计数归0,则删除该键,然后左边界向右移动一位
			charCount[s[left]]--
			if charCount[s[left]] == 0 {
				delete(charCount, s[left])
			}
			left++

			// 重新计算当前窗口最大字符计数,确保每次窗口有效性检查的正确性
			maxCount = 0
			for _, count := range charCount {
				if count > maxCount {
					maxCount = count
				}
			}
		}

		// 重新计数当前窗口有效长度,如果大于最大有效窗口长度,则更新最大有效窗口长度
		if right-left+1 > maxLength {
			maxLength = right - left + 1
		}
	}

	// 返回最终的有效窗口长度
	return maxLength
}
posted @ 2025-09-02 15:08  505donkey  阅读(21)  评论(0)    收藏  举报