解
第一步,萬年不變的查錯。如果給的string是null或長度為0,那么直接return。
public int lengthOfLongestSubstringKDistinct(String s, int k) {
if (s == null || s.length() == 0 || k == 0) {
return 0;
}
...
}
思路跟之前的幾道題很像,就是two pointer遍歷。題目要求最多有k個unique的字符,那就用個HashMap存一下出現(xiàn)過的字符,和出現(xiàn)過的次數(shù)。所以先做一下初始化。
int j = 0;
int maxLength = 0;
Map<Character, Integer> hash = new HashMap<>();
然后一個for循環(huán)做第一個pointer,while循壞做第二個pointer。
for (int i = 0; i < s.length(); i++) {
while (j < s.length() && hash.size() <= k) {
...
}
}
怎樣知道有沒有k個unique的字符呢?就是HashMap的size,一共有多少個key。如果到了k個,而且新的字符出現(xiàn),那就停下來。
if (hash.size() == k && !hash.containsKey(s.charAt(j))) {
break;
}
如果不到k個,那就把當前的這個放到hash里面,并且出現(xiàn)的次數(shù)+1。然后移動第二個pointer到下一個前向移動。
hash.put(s.charAt(j), hash.getOrDefault(s.charAt(j), 0) + 1);
j++;
這樣while循環(huán)就結(jié)束了。這時候只有可能出現(xiàn)兩種情況。
- j到了s的結(jié)尾,值為
s.length()
- 找到了k個unique的字符,并且如果加入當前字符,就會超過k個。
不管是哪種情況,都代表著,以s.charAt(i)
開頭的substring,已經(jīng)到了unique字符總數(shù)小于等于k的最大長度。所以更新一下最大長度。
maxLength = Math.max(maxLength, j - i);
然后,以i開頭的找完了,就把s.charAt(i)刪掉,讓for循環(huán)把i往前移,然后繼續(xù)找以i+1開頭的最大長度。把當前字符刪掉,就需要找到當前總共的個數(shù),去掉一個,然后去掉后是0,那么就代表著這個字符不存在于新的substring里了,所以就需要從HashMap里面刪掉整個key,這樣才不會影響一開始的Hashmap.size()
。
int currentCount = hash.get(s.charAt(i)) - 1;
if (currentCount == 0) {
hash.remove(s.charAt(i));
} else {
hash.put(s.charAt(i), currentCount);
}
最后整個for循環(huán)完成,返回最大長度。(小優(yōu)化:對比當前剩余字符串的長度和最大長度。如果最大長度超過剩余的長度,就直接返回。因為剩下的就算全部算上,也不可能超過最大長度。)
return maxLength;
完整的code
public class Solution {
/*
* @param s: A string
* @param k: An integer
* @return: An integer
*/
public int lengthOfLongestSubstringKDistinct(String s, int k) {
if (s == null || s.length() == 0 || k == 0) {
return 0;
}
int j = 0;
int maxLength = 0;
Map<Character, Integer> hash = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
while (j < s.length() && hash.size() <= k) {
if (hash.size() == k && !hash.containsKey(s.charAt(j))) {
break;
}
hash.put(s.charAt(j), hash.getOrDefault(s.charAt(j), 0) + 1);
j++;
}
maxLength = Math.max(maxLength, j - i);
int currentCount = hash.get(s.charAt(i)) - 1;
if (currentCount == 0) {
hash.remove(s.charAt(i));
} else {
hash.put(s.charAt(i), currentCount);
}
}
return maxLength;
}
}
分析
減了一個map,空間占用O(n),n為s的長度(字符數(shù))。因為最壞情況就是每個字符都放進map里面。時間是O(n),因為兩個pointer,都最多遍歷s各一次,加起來就是O(2n),也就是O(n)。
這道題不難,延續(xù)了two pointer的優(yōu)化解法,即一個for loop,一個while loop,j不隨著i的變化而重置,繼而達到O(n)的復雜度,而不是O(n2)。HashMap的去重來數(shù)有多少個字符,很容易想到,支持的加入和刪除,也是可以輕易想到的。隨意總的來說,這題不難。