[LeetCode] 424. Longest Repeating Character Replacement 解题报告

来源:互联网 发布:爱淘宝精品推荐 编辑:程序博客网 时间:2024/06/06 20:55

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 = 2Output:4Explanation:Replace the two 'A's with two 'B's or vice versa.

Example 2:

Input:s = "AABABBA", k = 1Output:4Explanation:Replace the one 'A' in the middle with 'B' and form "AABBBBA".The substring "BBBB" has the longest repeating letters, which is 4.

这个题看起来也不算特别难,和前面很多题都很类似,很容易想到动态规划,或者滑动窗口。这一题就是用滑动窗口来做。

思路:

滑动窗口的初始大小是k+1(因为小于k的根本不用判断,肯定可以),从左往右滑动。滑动的过程中,使用int[26]来记录下窗口中不同字符的个数,要满足题意的最好情况,就是选择个数最多的那个字符,然后把剩下所有的字符都换掉。于是,我们可以计算:

窗口大小-最多字符数量<k。如果是小于的, 说明k个字符已经足够满足将这个滑动窗口大小的窗口满足题意了,因此,也不需要往后滑动了,直接窗口大小++,开始新循环。

如果这里是大于,说明k不能满足当前窗口,继续滑动(滑动之后,需要再次计算当前窗口内所含的字符的数量。但是,这时候千万不要遍历计算,会超时的。直接将原来的首个字符减去,加上新的字符就可以了。例如:ABBAB,当前窗口是ABBA,a=2,b=2,向右滑动一格,a--,b++,即可),直到窗口检测完整个字符串,如果都不符合,也就不需要再加大窗口了,直接返回窗口大小-1,即可。

下面是代码,复杂度大致是O(N^2),个人猜测在N(N+1)/2左右:

public class Solution {boolean isReadDigit;public int characterReplacement(String s, int k) {if (s.length() <= k) {return s.length();}char[] cArr = s.toCharArray();int nMinWin = (int) ((double) k / 25 * 26);int nWindowSize = nMinWin > k + 1 ? nMinWin : k + 1;int[] nArrDigit = new int[26];// init find min window at index 0for (int i = 0; i < nWindowSize-1; i++) {nArrDigit[cArr[i] - 'A']++;}while (nWindowSize <= s.length()) {int nMax = -1;nArrDigit[cArr[nWindowSize-1]-'A']++;for (int i : nArrDigit) {nMax = i > nMax ? i : nMax;}if (nMax + k >= nWindowSize) {nWindowSize++;} else {break;}}while (nWindowSize <= s.length()) {boolean isFindLongest = false;isReadDigit = false;nArrDigit = new int[26];for (int i = 0; i <= cArr.length - nWindowSize; i++) {if (nWindowSize - findMaxDigit(i, nWindowSize, cArr, nArrDigit) <= k) {isFindLongest = true;break;}}if (isFindLongest) {nWindowSize++;} else {break;}}return nWindowSize - 1;}private int findMaxDigit(int nIndex, int nSize, char[] cArr, int[] nArrDigit) {if (!isReadDigit) {for (int i = nIndex; i < nIndex + nSize; i++) {nArrDigit[cArr[i] - 'A']++;isReadDigit = true;}} else {nArrDigit[cArr[nIndex - 1] - 'A']--;nArrDigit[cArr[nIndex + nSize - 1] - 'A']++;}int nMax = -1;for (int i : nArrDigit) {nMax = i > nMax ? i : nMax;}return nMax;}}

以上代码中,有几处优化说明一下:

  1. 14-28行,实际上做了一个预处理操作。给定窗口大小,我只判断从[0,0+窗口大小]这个范围内的字符串,如果符合,直接窗口大小++,循环,直到不符合。这之间,扩大窗口大小的时候,字符数组是用的新的字符++来得到的,而不是重新计算。(之所以要做这个初始化,因为存在很高的概率,从头开始的前面几项都是符合的)
  2. 对于窗口的初始大小,我不是用的k+1,而是用的下面的式子。这是因为,我们想算的是字母最多的数量是几,字母一共只有26了,不可能有k个完全不同的字符。在k大于52的时候,最多字符数量肯定是大于1的。下面的式子其实算的是一种极端最坏情况:
    (double) k / 25 * 26
0 0