滑动窗口题目:替换后的最长重复字符
文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法
- 思路和算法
- 证明
- 代码
- 复杂度分析
题目
标题和出处
标题:替换后的最长重复字符
出处:424. 替换后的最长重复字符
难度
6 级
题目描述
要求
给定一个字符串 s\texttt{s}s 和一个整数 k\texttt{k}k。可以选择字符串中的任意字符,并将其更改为任何其他大写英语字母。该操作最多可执行 k\texttt{k}k 次。
返回经过上述操作后可以获得的包含相同字母的最长子字符串的长度。
示例
示例 1:
输入:s="ABAB",k=2\texttt{s = "ABAB", k = 2}s = "ABAB", k = 2
输出:4\texttt{4}4
解释:将两个 ‘A’\texttt{`A'}‘A’ 替换为两个 ‘B’\texttt{`B'}‘B’,反之亦然。
示例 2:
输入:s="AABABBA",k=1\texttt{s = "AABABBA", k = 1}s = "AABABBA", k = 1
输出:4\texttt{4}4
解释:将中间的一个 ‘A’\texttt{`A'}‘A’ 替换为 ‘B’\texttt{`B'}‘B’,字符串变为 "AABBBBA"\texttt{"AABBBBA"}"AABBBBA"。子串 "BBBB"\texttt{"BBBB"}"BBBB" 有最长重复字母,长度为 4\texttt{4}4。
数据范围
- 1≤s.length≤105\texttt{1} \le \texttt{s.length} \le \texttt{10}^\texttt{5}1≤s.length≤105
- s\texttt{s}s 仅由大写英语字母组成
- 0≤k≤s.length\texttt{0} \le \texttt{k} \le \texttt{s.length}0≤k≤s.length
解法
思路和算法
任意长度为 111 的字符串都由相同的字符组成,因此在给定的字符串 sss 非空的情况下,答案不小于 111。
对于任意子字符串,用 maxCount\textit{maxCount}maxCount 表示子字符串中的最高频率字符的出现次数,如果子字符串的长度不超过 maxCount+k\textit{maxCount} + kmaxCount+k,则该子字符串中的非最高频率字符不超过 kkk 个,因此可以执行不超过 kkk 次替换操作将该子字符串中的其余字符都替换成最高频率字符,使得该子字符串由相同字符组成。
为了找到替换后的最长子字符串的长度,可以使用变长滑动窗口实现。用 [start,end][\textit{start}, \textit{end}][start,end] 表示滑动窗口,初始时 start=end=0\textit{start} = \textit{end} = 0start=end=0。将滑动窗口的右端点 end\textit{end}end 向右移动,移动过程中判断滑动窗口中的子字符串是否可以替换成由相同字符组成的子字符串,计算替换后的最长子字符串的长度。
使用哈希表记录滑动窗口中的每个字符的出现次数,用 maxCount\textit{maxCount}maxCount 表示子字符串中的最高频率字符的出现次数。对于每个右端点 end\textit{end}end,执行如下操作。
-
将 s[end]s[\textit{end}]s[end] 在哈希表中的次数加 111。
-
用 s[end]s[\textit{end}]s[end] 在哈希表中的次数更新 maxCount\textit{maxCount}maxCount。
-
当前滑动窗口 [start,end][\textit{start}, \textit{end}][start,end] 中的子字符串的长度是 end−start+1\textit{end} - \textit{start} + 1end−start+1,判断当前滑动窗口中的子字符串是否可以替换成由相同字符组成的子字符串。
-
如果 end−start+1≤maxCount+k\textit{end} - \textit{start} + 1 \le \textit{maxCount} + kend−start+1≤maxCount+k,则当前子字符串可以替换成由相同字符组成的子字符串,用 end−start+1\textit{end} - \textit{start} + 1end−start+1 更新替换后的最长子字符串的长度。
-
如果 end−start+1>maxCount+k\textit{end} - \textit{start} + 1 > \textit{maxCount} + kend−start+1>maxCount+k,则当前子字符串不能替换成由相同字符组成的子字符串,将 s[start]s[\textit{start}]s[start] 在哈希表中的次数减 111,然后将 start\textit{start}start 向右移动一位。
-
遍历结束之后,即可得到替换后的最长子字符串的长度。
实现方面,由于字符串 sss 由大写英语字母组成,因此可以创建一个长度为 262626 的数组代替哈希表。
证明
上述解法中,maxCount\textit{maxCount}maxCount 的值只会增加,不会减少,但是当滑动窗口的左端点 start\textit{start}start 向右移动时,滑动窗口中的字符的出现次数会减少,因此不能保证 maxCount\textit{maxCount}maxCount 是子字符串中的最高频率字符的出现次数。这并不影响上述解法可以得到正确的结果,理由如下。
对于每个右端点 end\textit{end}end,如果当前滑动窗口 [start,end][\textit{start}, \textit{end}][start,end] 中的子字符串可以替换成由相同字符组成的子字符串,则左端点 start\textit{start}start 不移动,否则将左端点 start\textit{start}start 向右移动一位,然后将右端点 end\textit{end}end 向右移动一位,因此对于每个右端点 end\textit{end}end,滑动窗口的大小一定是加 111 或不变。
如果滑动窗口的大小不变,则滑动窗口中的子字符串的长度不可能超过已经遍历过的替换后的最长子字符串的长度。只有当滑动窗口的大小加 111 时,才可能更新替换后的最长子字符串的长度,更新的条件是 end−start+1≤maxCount+k\textit{end} - \textit{start} + 1 \le \textit{maxCount} + kend−start+1≤maxCount+k。由于 end−start+1\textit{end} - \textit{start} + 1end−start+1 增加,kkk 不变,为了使条件成立,maxCount\textit{maxCount}maxCount 也要增加,即 maxCount\textit{maxCount}maxCount 被 s[end]s[\textit{end}]s[end] 在当前子字符串中的出现次数更新,因此 s[end]s[\textit{end}]s[end] 即为当前子字符串中的最高频率字符,其出现次数等于 maxCount\textit{maxCount}maxCount。
综上所述,当更新替换后的最长子字符串的长度时,maxCount\textit{maxCount}maxCount 的值一定被更新,此时 maxCount\textit{maxCount}maxCount 的值等于当前子字符串中的最高频率字符的出现次数,因此上述解法可以得到正确的结果。
代码
class Solution {public int characterReplacement(String s, int k) {int maxLength = 1;int[] counts = new int[26];int maxCount = 0;int start = 0, end = 0;int length = s.length();while (end < length) {char curr = s.charAt(end);counts[curr - 'A']++;maxCount = Math.max(maxCount, counts[curr - 'A']);if (end - start + 1 <= maxCount + k) {maxLength = Math.max(maxLength, end - start + 1);} else {char prev = s.charAt(start);counts[prev - 'A']--;start++;}end++;}return maxLength;}
}
复杂度分析
-
时间复杂度:O(n)O(n)O(n),其中 nnn 是字符串 sss 的长度。滑动窗口的左右端点最多各遍历字符串 sss 一次。
-
空间复杂度:O(∣Σ∣)O(|\Sigma|)O(∣Σ∣),其中 Σ\SigmaΣ 是字符集,这道题中 Σ\SigmaΣ 是全部大写英语字母,∣Σ∣=26|\Sigma| = 26∣Σ∣=26。哈希表需要 O(∣Σ∣)O(|\Sigma|)O(∣Σ∣) 的空间。