Given a string that consists of only uppercase English letters, you can replace any letter in the string with another letter at most k times. Find the length of a longest substring containing all repeating letters you can get after performing the above operations.
Note:
Both the string's length and k will not exceed 104.
Example 1:
Input: s = "ABAB", k = 2 Output: 4 Explanation: Replace the two 'A's with two 'B's or vice versa.
Example 2:
Input: s = "AABABBA", k = 1 Output: 4 Explanation: Replace the one 'A' in the middle with 'B' and form "AABBBBA". The substring "BBBB" has the longest repeating letters, which is 4.
我们可以对字符串做出最多k次更改。假设这个k是没有限制的无限大,我们把一个字符串以最小的改变来变为所有字符都一样,那么这个答案将是:
字符串的长度 - 字符串中出现最多的字符的出现次数
再假设我们做出最多k次更改。那么可以保持一个滑动窗口,来满足这个(做出最多k次更改使得滑动窗口中所有字符都一样)的限制。
解法如下:(滑动窗口字符串的长度 - 在滑动窗口字符串中出现最多的字符的出现次数) <= k
public int characterReplacement(String s, int k) {
int[] letters=new int[26];
int maxRepeatedLength=0;
int maxCount=0;
int start=0;
for(int end=0;end<s.length();end++){
char c=s.charAt(end);
letters[c-'A']++;
maxCount=Math.max(maxCount, letters[c-'A']);
while( (end-start+1-maxCount) > k ){ //end-start+1是子串(当前滑动窗口的)长度
letters[s.charAt(start)-'A']--;
start++;
}
maxRepeatedLength=Math.max(maxRepeatedLength, end-start+1);
}
return maxRepeatedLength;
}
思路如下:
这个问题会被转变为::
"最长的子串,使得 (length - max occurrence) <= k"
假设我们找到了这样一个有效的子串,那么下一步做什么?
- 如果在子串后再增加一个新的字符,仍能满足要求——我们很开心,只需要增加滑动窗口的长度
- 再增加新的字符后,不再满足要求。那么我们需要减少滑动窗口的长度吗?
——不,我们只需要向右移动滑动窗口,这样滑动窗口的长度不变。
因为以任何小于等于之前start来开始的滑动窗口,都不会比有机会获得一个比当前窗口长度更长的有效子串。 - 我们需要在每次滑动时,及时地更新maxCount吗?
——没有必要. 我们只专注于 "最长的"。那么有效子串的长度取决于什么?
Max Occurrence + k
我们只需要在maxCount变得更大时,更新maxCount。因为只有这种情况,我们才能产生一个更长的有效子串。
如果当前maxCount并没有超过历史的maxCount,那么我们将不能从当前的"start"开始构造出一个更长的有效子串。
因为我们只关心最长有效子串,所以滑动窗口将不会收缩。因此如果当前窗口的状态是一个无效的子串时,我们 要么把end增加一位,使得窗口长度增加;要么把start和end都增加一位,使得窗口长度不变,往右滑动一格。并且,我们只有当且仅当新字符的count超过历史最大count时,才增加窗口长度。
所以,我们不需要确切而及时地知道当前窗口的最大count为多少,我们只关心当前窗口的最大count是否超过历史最大count(而这只会发生在新字符出现 使得count增加时)