最多有k个不同字符的最长子字符串--JAVA实现

第一步,万年不变的查错。如果给的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的去重来数有多少个字符,很容易想到,支持的加入和删除,也是可以轻易想到的。随意总的来说,这题不难。

你可能感兴趣的:(笔试)