`

Leetcode - Longest Substring with At Most Two Distinct Characters

 
阅读更多
Given a string, find the length of the longest substring T that contains at most 2 distinct characters.

For example, Given s = “eceba”,

T is "ece" which its length is 3.
[分析] 滑动窗口法,窗口内始终仅保留至多两个相异字符组成的字符串。实现时有两个问题需要考虑:1)何时更新窗口的左边界 2)如何更新窗口的左边界。问题1简单,当前字符不在窗口内且窗口内已包含两种字符。问题2最关键,想清楚了该问题也就解决了。当前窗口的左边界字符必然是要被删除的,但需要全部清除窗口内所有该字符吗?不见得,考虑这个例子abaccc, 遍历到第一个c时若将a全部清除则求得的长度是3,但实际上应该是4。正确的做法是找到窗口内最后位置最靠左的字符,设其最末位置为mostLeftEndPos, 窗口的新左边界则为mostLeftEndPos + 1, 这种做法的正确性在于保证更新时清除的字符串最短。
参考博客中还给出了扩展题,至多保留K个相异字符。更多请阅读原文:
http://blog.csdn.net/whuwangyi/article/details/42451289

>>>第二次做
思路1:基本思想仍然是两指针式滑动窗口,实现上稍有差异。维护一个哈希表idxMap, key是字符,value是key最后出现的下标。activeTwo数组中两元素表示当前窗口中的两个相异字符。遍历字符串时若遇到新字符,意外着activeTwo中有个字符要被淘汰出窗口了,该淘汰哪个呢?易知当前位置的前一个字符应该是幸运儿,那么自然的另一个就是要被淘汰的。更新窗口前计算当前窗口长度并更新maxLen,然后更新窗口左指针为被淘汰字符最后出现下标的下一个位置。
思路2: 参考https://leetcode.com/discuss/21929/share-my-c-solution
代码清晰简洁,同longestK的差异在于使用数组代替HashMap。
注意当哈希表key是字符时使用数组代替HashMap,运行效率会稍高些。

// 第二次做的两种实现方式
// Method 2: map中value是count时, 这种从窗口起始位置开始遍历寻找第一个被淘汰的字符方式扩展性好些,可扩展到保留k个不同的情况
    public int lengthOfLongestSubstringTwoDistinct2(String s) {
        if (s == null) return 0;
        int maxDiffChars = 2;
        int maxLen = 0;
        int[] map = new int[256]; // key: char, value: occurs of key
        int i = 0, j = 0; // i & j is the start and end of the sliding window
        int count = 0; // number of diff char in the sliding window
        while (j < s.length()) {
            char curr = s.charAt(j);
            map[curr]++;
            if (map[curr] == 1) { // a new char
                count++;
                while (count > maxDiffChars) {
                    map[s.charAt(i)]--;
                    if (map[s.charAt(i)] == 0)
                        count--;
                    i++;
                }
            }
            maxLen = Math.max(maxLen, j - i + 1);
            j++;
        }
        return maxLen;
    }
    // Method 1: map中value是key最后一次出现的下标,下面的实现方式不能方便地扩展到窗口可保留最多k个字符的情况
    public int lengthOfLongestSubstringTwoDistinct1(String s) {
        if (s == null) return 0;
        if (s.length() < 3) return s.length();
        int[] idxMap = new int[256];
        for (int i = 0; i < 256; i++)
            idxMap[i] = -1;
        char[] activeTwo = new char[2];
        activeTwo[0] = s.charAt(0);
        int i = 0, j = 1;
        while (j < s.length() && s.charAt(j) == activeTwo[0])
            j++;
        if (j == s.length()) return s.length();
        activeTwo[1] = s.charAt(j);
        idxMap[activeTwo[0]] = j - 1;
        idxMap[activeTwo[1]] = j;
        j++;
        char survivor;
        int len = 1, maxLen = j;
        while (j < s.length()) {
            char curr = s.charAt(j);
            if (curr == activeTwo[0]) {
                idxMap[activeTwo[0]] = j;
            } else if (curr == activeTwo[1]) {
                idxMap[activeTwo[1]] = j;
            } else {
                survivor = s.charAt(j - 1);
                len = j - i;
                maxLen = Math.max(len, maxLen);
                if (activeTwo[0] == survivor) {
                    i = idxMap[activeTwo[1]] + 1;
                    activeTwo[1] = curr;
                    idxMap[activeTwo[1]] = j;
                } else {
                    i = idxMap[activeTwo[0]] + 1;
                    activeTwo[0] = curr;
                    idxMap[activeTwo[0]] = j;
                }
            }
            j++;
        }
        maxLen = Math.max(maxLen, j - i);
        return maxLen;
    }


public class LongestSubstringWithAtMost2DistinctChars {
    @Test
    public void testSolution() {
        Solution obj = new Solution();
        Assert.assertEquals(4, obj.longest2("abaa"));
        Assert.assertEquals(4, obj.longest2("abaccc"));
        Assert.assertEquals(4, obj.longestK("abaa", 2));
        Assert.assertEquals(4, obj.longestK("abaccc", 2));
        Assert.assertEquals(6, obj.longestK("abacccd", 3));
    }
}

class Solution {
    public int longest2(String s) {
        if (s == null || s.length() == 0)
            return 0;
        int start = 0;
        int N = s.length();
        HashMap<Character, Integer> map = new HashMap<Character, Integer>(2);
        map.put(s.charAt(0), 0);
        int max = 1;
        for (int i = 1; i < N; i++) {
            char c = s.charAt(i);
            if (!map.containsKey(c) && map.size() == 2) {
                // update left bound of sliding window
                int mostLeftEndPos = i - 1;
                char toBeRemoved = ' ';
                for (Character key : map.keySet()) {
                    if (map.get(key) < mostLeftEndPos) {
                        mostLeftEndPos = map.get(key);
                        toBeRemoved = key;
                    }
                }
                start = mostLeftEndPos + 1;
                map.remove(toBeRemoved);
            }
            map.put(c, i);
            max = Math.max(max, i - start + 1);
        }
        return max;
    }
    //k 比较大时遍历map就比较低效,不如从start开始寻找第一个最后位置跑出窗口的字符
    public int longestK(String s, int k) {
        if (s == null || s.length() == 0)
            return 0;
        int N = s.length();
        int start = 0;
        int max = 0;
        
        // c: letter; value: the number of occurrences.  
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        for (int i = 0; i < N; i++) {
            char c = s.charAt(i);
            if (map.containsKey(c)) {
                map.put(c, map.get(c) + 1);
            } else {
                map.put(c, 1);
                while (map.size() > k) {
                    char startChar = s.charAt(start++);
                    int count = map.get(startChar);
                    if (count > 1) {
                        map.put(c, count - 1);
                    } else {
                        map.remove(startChar);
                    }
                }
           }
            max = Math.max(max, i - start + 1);
        }
        return max;
    }
}
分享到:
评论

相关推荐

    java-leetcode题解之Longest Substring with At Most Two Distinct

    在Java实现的LeetCode题解系列中,本篇文章专注解析了“最多包含两个不同字符的最长子串”这一编程题目。该问题属于字符串处理和滑动窗口算法的范畴,要求编写一个函数,找出给定字符串中不含超过两个不同字符的最长...

    js-leetcode题解之159-longest-substring-with-at-most-two-distinct

    题目"159-longest-substring-with-at-most-two-distinct"是LeetCode算法题目之一。本题要求解的是找出在给定字符串中,具有最多两个不同字符的最长子串的长度。 2. 题目要求: - 输入是一个字符串,保证只包含...

    leetcode1-240题中文题解,md格式,java

    10. leetcode-115-Distinct-Subsequences.md:第115题,不同的子序列,涉及到动态规划和字符串处理。 这些题目覆盖了各种算法和数据结构,包括字符串处理、动态规划、树结构遍历、数组处理、位运算、深度优先搜索、...

    LeetCode最全代码

    371 | [Sum of Two Integers](https://leetcode.com/problems/sum-of-two-integers/) | [C++](./C++/sum-of-two-integers.cpp) [Python](./Python/sum-of-two-integers.py) | _O(1)_ | _O(1)_ | Easy | LintCode | ...

    Leetcode book刷题必备

    11. Longest Substring with At Most Two Distinct Characters:找出不含有超过两个不同字符的最长子串的长度。 12. Missing Ranges:给定一个有序整数列表和两个端点值,找出缺失的范围。 13. Longest Palindromic ...

    leetcode双人赛-LeetCode:力码

    leetcode双人赛 1. Pattern: Sliding Window,滑动窗口类型 滑动窗口类型的题目经常是用来执行数组或是链表上某个区间(窗口)上的操作。比如找最长的全为1的子数组长度。滑动窗口一般从第一个元素开始,一直往右边...

    leetcode分类-LeetCode:力码

    leetcode 分类 LeetCode Progress 128/154 Other Solutions C++,有详细思路解释 python,部分有解释 Java,部分有解释 Java Associated Documents and Resources Peter norvig神牛Python代码写的很飘逸,果然是有LISP...

    常见算法题答案及解析

    11. Longest Substring with At Most Two Distinct Characters:找出含有最多两个不同字符的最长子串。 12. Missing Ranges:给定一个无序的整数数组,找出所有缺失的区间。 13. Longest Palindromic Substring:找...

Global site tag (gtag.js) - Google Analytics