我的问题:写是写出来了,但是用的方法是时间复杂度过高。
1. 先遍历整个string,找出所有char的频率。
2. initialize一个max值,作为储存值和update max的标准。
3. 外loop:对于26个字母,每个都loop一遍。
4. 内loop:对于input string里的每一个char,都loop一遍。对于当前的char,以当前为起点,根据k值和通过外loop选定的char来确定终结点,然后用start-end的string长度和max进行比较。如果比max大则更新。这样内外结合的目的是尝试以每个char开头的所有可能性,用来避免以左边为选定的当前char,再进行loop的暴力解决方案。
5. 这样做的后果是,会出现time limit exceed. 因为有两个大型loop,这道题的time complexity是O(n + 26*n^2) = O(n^2)。这道题的space complexity是O(26) = O(1)。节省空间的后果,就是以超长的时间奔跑作为代价。
6. 对sliding window这个操作极度不熟悉。对于一个cs学生来说真是惭愧。
美妙的答案:用了sliding window的做法,类似题目可见#3 -- Longest substring without repeating characters (https://leetcode.com/problems/longest-substring-without-repeating-characters/)
1. 直接用一个大loop,把每个char都loop一遍。
2. 有一个start point和一个end point。start point是先固定,到特殊情况下再移动;end point就是用遍历的方法,遍历到input string中的每一个char。
3. start = 0, end = 0,从0来开始操作end point。然后在整个操作过程中,用一个有26个格子的array来记录当前sliding window里的各个字母的出现频率。并且用一个叫做max的变量来储存当前window下的最大频率。(我们只需要在这里记录频率,不需要记录具体的字母,因为这道题只关心要改掉多少个char,并不关心是以哪个char为中心去改掉别的char。)
![](/qrcode.jpg)
4. 如果每读一个新的end point,就更新max,找到当前window里的最大频率。然后根据end - start + 1 - max来计算可以改掉的字符数量。如果end - start + 1 - max <= k,这就意味着还有空间,可以继续移动end point。如果 > k,这就意味着没有空间了,这个时候只能通过移动start point来调整sliding window。在这种情况下就要把start point上的这个char在当前频率表里的出现频率 - 1,然后尝试去继续expand end point。
5. 在每次当前操作结束后,计算一下当前window里的string长度,然后取这么多长度里的最大长度,return。
Time complexity: O(n)
Space complexity: O(26) = O(1) because we know that there are only 26 alphabet chars can be input.
Sliding window的本质:
1. start point and end point (two points)
2. a data structure to store the current string
3. From index 0, constantly move the ending point to the end of the string. Check the constrains every time we move the end point. If the constrain is violated, move the start point one index to the right, and delete that char from the data structure.
4. Constantly check before each manipulation, try to get the largest (usually?) thing you want.
自己修正后并accepted的代码如下:
1 class Solution { 2 public int characterReplacement(String s, int k) { 3 int[] frequency = new int[26]; 4 for(int i = 0; i < 26; i++){ 5 frequency[i] = 0; 6 } 7 8 int max = 0; // used to record the max length of substring 9 int count = 0; // used to record the largest frequency in current window 10 int start = 0; 11 int end = 0; 12 13 for (end = 0; end < s.length(); end++){ 14 frequency[s.charAt(end) - 'A']++; 15 count = Math.max(count, frequency[s.charAt(end) - 'A']); 16 if(end - start + 1 - count > k){ 17 // delete start char in frequency array and move start point 18 frequency[s.charAt(start) - 'A']--; 19 start++; 20 } 21 max = Math.max(max, end - start + 1); 22 } 23 24 return max; 25 } 26 }