原题
解
第一步,万年不变的查错。如果给的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存一下出现过的字符,和出现过的次数。所以先做一下初始化。
int j = 0;
int maxLength = 0;
Map hash = new HashMap<>();
然后一个for循环做第一个pointer,while循坏做第二个pointer。
for (int i = 0; i < s.length(); i++) {
while (j < s.length() && hash.size() <= k) {
...
}
}
怎样知道有没有k个unique的字符呢?就是HashMap的size,一共有多少个key。如果到了k个,而且新的字符出现,那就停下来。
if (hash.size() == k && !hash.containsKey(s.charAt(j))) {
break;
}
如果不到k个,那就把当前的这个放到hash里面,并且出现的次数+1。然后移动第二个pointer到下一个前向移动。
hash.put(s.charAt(j), hash.getOrDefault(s.charAt(j), 0) + 1);
j++;
这样while循环就结束了。这时候只有可能出现两种情况。
- j到了s的结尾,值为
s.length()
- 找到了k个unique的字符,并且如果加入当前字符,就会超过k个。
不管是哪种情况,都代表着,以s.charAt(i)
开头的substring,已经到了unique字符总数小于等于k的最大长度。所以更新一下最大长度。
maxLength = Math.max(maxLength, j - i);
然后,以i开头的找完了,就把s.charAt(i)删掉,让for循环把i往前移,然后继续找以i+1开头的最大长度。把当前字符删掉,就需要找到当前总共的个数,去掉一个,然后去掉后是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循环完成,返回最大长度。(小优化:对比当前剩余字符串的长度和最大长度。如果最大长度超过剩余的长度,就直接返回。因为剩下的就算全部算上,也不可能超过最大长度。)
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 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的长度(字符数)。因为最坏情况就是每个字符都放进map里面。时间是O(n),因为两个pointer,都最多遍历s各一次,加起来就是O(2n),也就是O(n)。
这道题不难,延续了two pointer的优化解法,即一个for loop,一个while loop,j不随着i的变化而重置,继而达到O(n)的复杂度,而不是O(n2)。HashMap的去重来数有多少个字符,很容易想到,支持的加入和删除,也是可以轻易想到的。随意总的来说,这题不难。