滑动窗口写法"hi移一步、lo移多步",可以解"至多K"问题atMost(K)

  • ”刚好K“问题exactly(K)=atMost(K)-atMost(K-1)

联想:atLeast(K)问题只能解全正数数组的最短子段,需要 1.全正数 2.atLeastK 3.求最短。见子段和>=T

无重复字符的最长子段

窗口内至多一个

  • https://leetcode.com/problems/longest-substring-without-repeating-characters/
int lengthOfLongestSubstring(string s) {
    const int N = s.size();
    unordered_map<char, int> cnt;
    int ans = 0;
    for (int hi = 0, lo = 0; hi < N; hi++) {
        cnt[s[hi]]++;
        while (cnt[s[hi]] > 1) {
            cnt[s[lo]]--;
            lo++;
        }
        ans = max(ans, hi - lo + 1);
    }
    return ans;
}

至多K个不同字符的最长子段

  • https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/
int lengthOfLongestSubstringKDistinct(string s, int k) {
    unordered_map<int, int> cnt; // char=>count
    int ans = 0;
    for (int hi = 0, lo = 0; hi < s.size(); hi++) {
        if (cnt[s[hi]]++ == 0) k--;
        while (k < 0) {
            if (--cnt[s[lo]] == 0) k++;
            lo++;
        }
        ans = max(ans, hi - lo + 1);
    }
    return ans;
}

把01数组的<=K位0改成1,连续1的最长子段长

int longestOnes(vector<int>& A, int K) {
    const int N = A.size();
    int zeroCnt = 0, ans = INT_MIN;
    for (int hi = 0, lo = 0; hi < N; hi++) {
        if (A[hi] == 0) zeroCnt++;
        while (zeroCnt > K) {
            if (A[lo++] == 0) zeroCnt--;
        }
        ans = max(ans, hi - lo + 1);
    }
    return ans;
}

含K个不同数的子段个数

int subarraysWithKDistinct(vector<int>& A, int K) {
    return atMost(A, K) - atMost(A, K-1);
}

int atMost(vector<int>& A, int K) {
    const int N = A.size();
    unordered_map<int, int> cnt; // num=>count
    int ans = 0;
    for (int hi = 0, lo = 0; hi < N; hi++) {
        if (cnt[A[hi]]++ == 0) K--;
        while (K < 0) {
            if (--cnt[A[lo]] == 0) K++;
            lo++;
        }
        ans += hi - lo + 1;
    }
    return ans;
}

长为K的最小和子段

简单的情况,可不用atMost(K)-atMost(K-1)写法

int maxScore(vector<int>& cardPoints, int k) {
    // 变为:找长=N-k的最小和子段
    const int N = cardPoints.size();
    int totalSum = 0, subSum = 0, minSum = INT_MAX;
    for (int hi = 0, lo = 0; hi < N; hi++) {
        totalSum += cardPoints[hi];
        subSum += cardPoints[hi];
        if (hi - lo + 1 > N - k) {
            subSum -= cardPoints[lo];
            lo++;
        } 
        if (hi - lo + 1 == N - k) {
            minSum = min(minSum, subSum);
        }
    }
    return totalSum - minSum;
}