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

浙公网安备 33010602011771号