刷题:sliding window

cookbook

  • 左右指针不可以回头
  • 找到描述窗口的数据结构

1、无重复字符的最长子串

    // 用HashMap来维护窗口大小, 因为没有删除map里的value,所以窗口可能包含left左区间的值
    // 需要注意left的取值问题, 案例"abba"
    class Solution {
        public int lengthOfLongestSubstring(String s) {
            if (s.length() == 0) return 0;

            int left = 0;
            int max = 0;
            HashMap map = new HashMap<>();
            for (int right = 0; right < s.length(); right++) {
                if (map.containsKey(s.charAt(right))) {
                    left = Math.max(left, map.get(s.charAt(right)) + 1);   // 注意这里
                }
                map.put(s.charAt(right), right);
                max = Math.max(max, right - left + 1);
            }
            return max;
        }
    }

    // 用set维护窗口,需要删除,较为麻烦
    class Solution {
        public int lengthOfLongestSubstring(String s) {
            int i = 0;
            int j = -1;
            int len = j - i + 1;
            Set  set = new HashSet<>();
            while (j < s.length()-1) {
                j++;
                char c = s.charAt(j);

                if (!set.contains(c)) {
                    len = Math.max(len, j-i+1);
                    set.add(c);
                } else {
                    while (s.charAt(i++) != c) {
                        set.remove(s.charAt(i-1));
                    }
                }
            }
            return len;
        }
    }

2、串联所有单词的子串

class Solution {
    public List findSubstring(String s, String[] words) {
        Map counts = new HashMap<>();
        for (String str : words) counts.put(str, counts.getOrDefault(str, 0)+1);
        int n = s.length(); int len = words[0].length(); int num = words.length;
        List res = new ArrayList<>();

        for (int i = 0; i < n - num * len + 1; i++) {
            Map seen = new HashMap<>();
            int j = 0;
            while (j < num) {
                String curStr = s.substring(i + j * len, i + (j + 1) * len);
                if (counts.containsKey(curStr)) {
                    seen.put(curStr, seen.getOrDefault(curStr, 0) + 1);
                    if (seen.get(curStr) > counts.get(curStr)) {
                        break;
                    }
                    
                } else {
                    break;
                }
                j++;
            }
            if (j == num) {
                res.add(i);    // 第一次居然把这里添加了j
            }
        }
       return res;
    }
}

3、最小覆盖子串

// 用count数组来计数, t的长度表示需要的距离,维护好这两个量
    class Solution {
        public String minWindow(String s, String t) {
            int[] count = new int[128];
            for (char c : t.toCharArray()) count[c]++;
            int size = t.length();
            int N = s.length();
            int left = 0;
            int right = 0;
            int head = 0;
            int minLen = N + 1;
            while (right < N) {
                if (count[s.charAt(right++)]-- > 0) size--;
                while (size == 0) {
                    if (right - left < minLen) {
                        head = left;
                        minLen = right - head;
                    }
                    if (count[s.charAt(left++)]++ == 0) size++;
                }
            }
            return minLen == N + 1 ? "" : s.substring(head, head + minLen);
        }
    }

4、 重复的DNA序列

class Solution {
        public List findRepeatedDnaSequences(String s) {
            Set set = new HashSet<>();
            Set res = new HashSet<>();
            for (int i = 10; i <= s.length(); i++) {
                String cur = s.substring(i - 10, i);
                if (set.contains(cur)) {
                    res.add(cur);
                } else {
                    set.add(cur);
                }
            }
            return new ArrayList<>(res);
        }
    }

5、长度最小的子数组

    class Solution {
        public int minSubArrayLen(int target, int[] nums) {
            if (nums == null || nums.length <= 0) return 0;
            int minLength = nums.length + 1;
            int left = 0;
            int right = 0;
            int sum = 0;

            while (right < nums.length) {
                sum += nums[right++];
                while (sum >= target) {
                    minLength = Math.min(minLength, right - left);
                    sum -= nums[left++];
                }
            }
            return minLength == nums.length + 1 ? 0 : minLength;
        }
    }

6、存在重复元素 II

class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        HashSet set = new HashSet<>();
        for(int i = 0; i < nums.length; i++) {
            if(set.contains(nums[i])) {
                return true;
            }
            set.add(nums[i]);
            if(set.size() > k) {
                set.remove(nums[i - k]);
            }
        }
        return false;
    }
}

7、存在重复元素 III,*

// 用hash桶来判断,比较巧妙,主要桶id的计算
    class Solution {
        public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
            long size = t + 1;
            HashMap map = new HashMap<>();

            for (int i = 0; i < nums.length; i++) {
                long cur = nums[i];
                long id = cur >= 0 ? cur / size : (cur + 1) / size - 1;
                if (map.containsKey(id)) return true;
                if (map.containsKey(id - 1) && cur - map.get(id - 1) <= t) return true;
                if (map.containsKey(id + 1) && map.get(id + 1) - cur <= t) return true;
                map.put(id, cur);
                if (i >= k) {
                    long left = nums[i - k];
                    long pre = left >= 0 ? left / size : (left + 1) / size - 1;
                    map.remove(pre);
                }
            }
            return false;
        }
    }
    
    // 用TreeSet维护窗口,方便查找
    class Solution0 {
        public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
            TreeSet set = new TreeSet<>();

            for (int i = 0; i < nums.length; i++) {
                Long ceiling = set.ceiling((long)nums[i] - (long)t);
                if (ceiling != null && ceiling <= (long)nums[i] + (long)t) {
                    return true;
                }
                set.add((long)nums[i]);
                if (i >= k) {
                    set.remove((long)nums[i-k]);
                }
            }
            return false;
        }
    }

8、滑动窗口最大值

    class Solution {
        public int[] maxSlidingWindow(int[] nums, int k) {
            Deque deque = new ArrayDeque<>();
            int[] res = new int[nums.length - k + 1];
            for (int i = 0; i < nums.length; i++) {
                int inputVal = nums[i];
                while (!deque.isEmpty() && deque.peekLast() <  inputVal) {
                    deque.pollLast();
                }
                deque.offer(inputVal);
                if (i >= k -1) {
                    res[i - k + 1] = deque.peek();
                    if (deque.peek() == nums[i-k+1]) {
                        deque.poll();
                    }
                }
            }
            return res;
        }
    }

9、至少有 K 个重复字符的最长子串,*

// 滑动窗口+递归,先统计s中数量,小于k的是分割线,不包含在子串中
class Solution {
    public int longestSubstring(String s, int k) {
        int N = s.length();
        int[] counts = new int[26];
        for (char c : s.toCharArray()) {
            counts[c - 'a']++;
        }
        boolean flag = true;
        for (int cnt : counts) {
            if (cnt > 0 && cnt < k) flag = false;
        }
        if (flag) return N;
        int left = 0;
        int right = 0;
        int res = 0;
        while (right < N) {
            int index = s.charAt(right) - 'a';
            if (counts[index] < k) {
                String sub = s.substring(left, right);
                res = Math.max(res, longestSubstring(sub, k));
                left = right + 1;
            }
            right++;
        }
        res = Math.max(res, longestSubstring(s.substring(left), k));
        return res;
    }
}

10、替换后的最长重复字符,*

    // 用count数组描述窗口,curmax维护窗口的最大字符数量,right - left表示历史最大结果,只增不减
    class Solution {
        public int characterReplacement(String s, int k) {
            int N = s.length();
            int left = 0;
            int right;
            int[] count = new int[26];
            int curmax = 0;

            for (right = 0; right < N; right++) {
                curmax = Math.max(curmax, ++count[s.charAt(right) - 'A']);
                int curLength = right - left + 1;
                if (curLength - curmax > k) {
                    count[s.charAt(left++) - 'A']--;
                }
            }
            return right - left;
        }
    }

11、找到字符串中所有字母异位词,*

// 和76题最小覆盖字串相比,这题的长度和目标是一样的,所以无须维护distance,有一个不是p中的字符,就会从头开始
class Solution {
        public List findAnagrams(String s, String p) {
            List res = new ArrayList<>();
            if (s.length() < p.length()) return res;

            int[] needFreq = new int[26];
            int[] windowFreq = new int[26];
            
            for (int i = 0; i < p.length(); i++) {
                needFreq[p.charAt(i) - 'a']++;
            }
            
            int left = 0;
            int right = 0;
            while (right < s.length()) {
                int rightCharIndex = s.charAt(right++) - 'a';
                windowFreq[rightCharIndex]++;
                while (left < s.length() && windowFreq[rightCharIndex] > needFreq[rightCharIndex]) {
                    windowFreq[s.charAt(left) - 'a']--;
                    left++;
                }
                if (right - left == p.length()) {
                    res.add(left);
                }  
            }
            return res;
        }
    }

12、滑动窗口中位数,涉及堆,先遗留


13、字符串的排列

// 和"找到字符串中所有字母异位词"一样
class Solution {
        public boolean checkInclusion(String s1, String s2) {
            
            int[] needs = new int[26];
            for (int i = 0; i < s1.length(); i++) {
                needs[s1.charAt(i) - 'a']--;
            }
            int left = 0;
            int right = 0;
            while (right < s2.length()) {
                needs[s2.charAt(right) - 'a']++;
                while (needs[s2.charAt(right) - 'a'] > 0) {
                    needs[s2.charAt(left++) - 'a']--;
                }
                if (right - left + 1 == s1.length()) return true;
                right++;
            }
            return false;  
        }
    }

14、最小区间

class Solution {
        public int[] smallestRange(List> nums) {
            HashMap> insides = new HashMap<>();    // 区间值,nums元素下标映射
            int max = Integer.MIN_VALUE;
            int min = Integer.MAX_VALUE;
            int size = nums.size();
            for (int i = 0 ; i < size; i++) {
                for (int num : nums.get(i)) {
                    List list = insides.getOrDefault(num, new ArrayList());
                    list.add(i);
                    insides.put(num, list);
                    max = Math.max(max, num);
                    min = Math.min(min, num);
                }
            }
            int[] freq = new int[size];    // 数组下标出现的次数
            int resLeft = min;
            int resRight = max;
            int count = 0;
            int left = min;
            int right = min;
            while (right <= max) {          // 注意这里是等于
                if (insides.containsKey(right)) {
                    for (int index : insides.get(right)) {
                        freq[index]++;
                        if (freq[index] == 1) count++;
                    }
                    while (count == size) {
                        if (right - left < resRight - resLeft) {    // 错误记录,既然把这里left和right顺序搞反了
                            resLeft = left;
                            resRight = right;
                        }
                        if (insides.containsKey(left)) {
                            for (int leftIndex : insides.get(left)) {
                                freq[leftIndex]--;
                                if (freq[leftIndex] == 0) count--;
                            }
                        }
                        left++;
                    }

                }
                right++;
            }
            return new int[] {resLeft, resRight};
        }
    }

15、子数组最大平均数 I

class Solution {
        public double findMaxAverage(int[] nums, int k) {
            int sum = 0;
            int maxSum = Integer.MIN_VALUE;
            for (int i = 0; i < nums.length; i++) {
                sum += nums[i];
                if (i >= k - 1) {
                    maxSum = Math.max(maxSum, sum);
                    sum -= nums[i - k + 1];
                }
            }
            return (maxSum * 1.0) / k;
        }
    }

16、乘积小于K的子数组

// 每次加上以右结尾子数组的个数
class Solution {
        public int numSubarrayProductLessThanK(int[] nums, int k) {
            if (k <= 1) return 0;
            int left = 0;
            int cnt = 0;
            int prod = 1;
            for (int right = 0; right < nums.length; right++) {
                prod *= nums[right];
                while (prod >= k) {
                    prod /= nums[left++];
                }
                cnt += right - left + 1;
            }
        return cnt;
        }
    }

17、最长重复子数组

// 过程可以看成两把尺子的头尾相接并相想移动
    class Solution {
        public int findLength(int[] nums1, int[] nums2) {
            return nums1.length < nums2.length ? findMax(nums1, nums2) : findMax(nums2, nums1);
        }
        public int findMax(int[] A, int[] B) { // 假定A.length <= B.length;
            int res = 0;
            int aLen = A.length;
            int bLen = B.length;
            for (int len = 1; len < aLen; len++) {
                res = Math.max(res, findMax(A, 0, B, bLen - len, len));
            }
            for (int i = 1; i < bLen - aLen; i++) {
                res = Math.max(res, findMax(A, 0, B, bLen - aLen - i, aLen));
            }
            for (int i = 0; i < aLen; i++) {
                res = Math.max(res, findMax(A, i, B, 0, aLen - i));
            }
            return res;
        }

        public int findMax(int[] A, int aStart, int[] B, int bStart, int len) {
            int res = 0;
            int sum = 0;
            for (int i = 0; i < len; i++) {
                if (A[aStart + i] == B[bStart + i]) {
                    sum++;
                } else if (sum > 0) {
                    res = Math.max(res, sum);
                    sum = 0;
                }
            }
            return Math.max(res, sum);   // 注意这里sum最后可能不为0;
        }
    }

18、和至少为 K 的最短子数组

// 利用前缀和转化为求窗口内两个值差大于K的最小子数组长度
    class Solution {
        public int shortestSubarray(int[] nums, int k) {
            int N = nums.length;
            long[] preSum = new long[N + 1];
            for (int i = 0; i < N; i++) {
                preSum[i + 1] = preSum[i] + (long)nums[i];
            }
            Deque deque = new LinkedList<>();
            int min = N + 1;
            int right = 0;

            while (right < N + 1) {
                long cur = preSum[right];
                while (!deque.isEmpty() && preSum[deque.getLast()] >= cur) {
                    deque.removeLast();
                }

                while (!deque.isEmpty() && cur >= preSum[deque.getFirst()] + k) {
                    min = Math.min(min, right - deque.removeFirst());
                }
                deque.addLast(right);
                right++;
            }
            return min < N + 1 ? min : -1;
        }
    }

19、水果成篮

class Solution {
        public int totalFruit(int[] fruits) {
            int left = 0;
            int right = 0;
            Map cnt = new HashMap<>();
            
            for (; right < fruits.length;right++) {
                cnt.put(fruits[right], cnt.getOrDefault(fruits[right], 0) + 1);
                if (cnt.size() > 2) {
                    cnt.put(fruits[left], cnt.get(fruits[left]) - 1);
                    cnt.remove(fruits[left++], 0);
                }
            }
        return right - left;    
        }
    }

20、和相同的二元子数组 ,前缀和+滑动窗口待求解。

// 前缀和+哈希表解,转化为p[j] - p[i] = k
    class Solution {
        public int numSubarraysWithSum(int[] nums, int goal) {
            int sum = 0;
            int res = 0;
            Map cnt = new HashMap<>(); // 前缀和 的cnt表

            for (int i = 0; i < nums.length; i++) {
                cnt.put(sum, cnt.getOrDefault(sum, 0) + 1);
                sum += nums[i];
                res += cnt.getOrDefault(sum - goal, 0);
            }
            return res;
        }
    }

// 转化为最多有goal个1的子数组问题求解
    class Solution {
        public int numSubarraysWithSum(int[] nums, int goal) {
            if (goal <= 0) return solve(nums,0);
            return solve(nums, goal) - solve(nums, goal - 1);
        }

        // 最多有goal个1的子数组
        public int solve(int[] nums, int goal) {
            int res = 0;
            int sum = 0;
            int left = 0;
            int right = 0;
            while (right < nums.length) {
                sum += nums[right++];
                while (sum > goal) {
                    sum -= nums[left++];
                }
                res += right - left;
            }
            return res;
        }
    }

21、最长湍流子数组

class Solution {
    public int maxTurbulenceSize(int[] arr) {
        if (arr.length == 0) {
            return 0;
        }
        int left = 0;
        int right = 1;   // 注意right从1开始
        int pre = 0;
        int res = 1;    // 这里注意为1
        
        while (right < arr.length) {
            int diff = arr[right] - arr[right - 1];
            if ((pre < 0 && diff < 0) || (pre > 0 && diff > 0)) {
                left = right - 1;
            }
            if (diff == 0) {
                left = right;
            }
            right++;
            res = Math.max(res, right - left);
            pre = diff;    // 记得改变pre的值
        }
        return res;
    }
}

22、K 个不同整数的子数组, 缺差分数组解法

// 转化为求最多包含k个不同整数的子数组问题求解
class Solution {
        public int subarraysWithKDistinct(int[] nums, int k) {
            
            return mostKsubrrays(nums, k) - mostKsubrrays(nums, k - 1);
        }

        public int mostKsubrrays(int[] nums, int k) {
            if (nums.length == 0 || k <= 0) return 0;
            int[] freq = new int[nums.length+1];
            int left = 0;
            int right = 0;
            int cnt = 0;
            int res = 0;
            for (; right < nums.length; right++) {
                freq[nums[right]]++;
                if (freq[nums[right]] == 1) cnt++;
                while (cnt > k) {
                    freq[nums[left]]--;
                    if (freq[nums[left]] == 0) cnt--;
                    left++;
                }
                res += right - left + 1;
            }
            return res;
        }
    }

23、K 连续位的最小翻转次数,*

class Solution {
        public int minKBitFlips(int[] nums, int k) {

            Queue queue = new ArrayDeque<>();
            int res = 0;

            for (int i = 0; i < nums.length; i++) {
                if (!queue.isEmpty() && i - queue.peek() > k - 1) {
                    queue.poll();
                }
                if (queue.size() % 2 == nums[i]) {
                    if (i + k > nums.length) return -1;
                    queue.add(i);
                    res++;
                }
            }
            return res;
        }
    }

24、最大连续1的个数 III

// 维护k就行
class Solution {
    public int longestOnes(int[] nums, int k) {
        if (nums.length == 0) return 0;
        int left = 0;
        int right = 0;
        while(right < nums.length) {
            if (nums[right++] == 0) k--;
            if (k < 0 && nums[left++] == 0) k++;
        }
        return right -left;
    }
}

25、爱生气的书店老板

// 先加不生气的,然后把不生气的customer值变为0,转化为求固定窗口大小的最大值
class Solution {
    public int maxSatisfied(int[] customers, int[] grumpy, int minutes) {
        int N = customers.length;
        int ans = 0;
        for (int i = 0; i < N; i++) {
            if (grumpy[i] == 0) {
                ans += customers[i];
                customers[i] = 0;
            }
        }

        int left = 0;
        int max = 0;
        int cur = 0;
        for (int right = 0; right < N; right++) {
            cur += customers[right];
            if (right - left + 1 > minutes) cur -= customers[left++];
            max = Math.max(max, cur);
        }
        return max + ans;
    }
}

class Solution {
        public int maxSatisfied(int[] customers, int[] grumpy, int minutes) {
            int left = 0;
            int right = 0;
            int max = 0;
            int cur = 0;

            for (int i = 0; i < grumpy.length; i++) {
                cur += grumpy[i] == 0 ? customers[i] : 0;
            }

            for (; right < customers.length; right++) {
                if (grumpy[right] == 1) {
                    cur += customers[right];
                    while (right - left + 1 > minutes) {
                        if (grumpy[left] == 1) {
                            cur -= customers[left];
                        }
                        left++;
                    }
                }
                max = Math.max(max, cur);
            }
            return max;
        }
    }

26、尽可能使字符串相等

// 常规滑动窗口的题,一次过
    class Solution {
        public int equalSubstring(String s, String t, int maxCost) {
            int N = s.length();
            int[] diff = new int[N];
            for (int i = 0; i < N; i++) {
                diff[i] = Math.abs(s.charAt(i) - t.charAt(i));
            }
            int left = 0;
            int right = 0;
            int sum = 0;
            int res = 0;
            while (right < N) {
                sum += diff[right++];
                while (sum > maxCost) {
                    sum -= diff[left++];
                }
                res = Math.max(res, right - left);
            }
            return res;
        }
    }

27、替换子串得到平衡字符串

// 此题和76题类似,先把问题转化为求一个窗口内包含某些字符串的子串最短长度
    class Solution {
        public int balancedString(String s) {
            int N = s.length();
            int quarter = N / 4;
            int[] cnt = new int[26];
            int need = 0;
            for (char c : s.toCharArray()) {
                if (++cnt[c - 'A'] > quarter) need++;
            }
            if (need == 0) return 0;
            int size = need;
            int res = N + 1;

            int left = 0;
            int right = 0;
            while (right < N) {
                int cur = s.charAt(right++) - 'A';
                if (cnt[cur]-- > quarter) need--;
                while (need == 0) {
                    res = Math.min(res, right - left);     // 第一次居然在这里把res写成size了,我该打
                    int leftIndex = s.charAt(left) - 'A';
                    if (++cnt[leftIndex] > quarter) need++;
                    left++;
                }
            }
            return res;
        }
    }

28、可获得的最大点数

// 转化为求窗口的最小值
    class Solution {
        public int maxScore(int[] cardPoints, int k) {
            int N = cardPoints.length;
            int wSize = N - k;
            int sum = 0;
            for (int i = 0; i < N - k; i++) {
                sum += cardPoints[i];
            }
            int minSum = sum;

            for (int i = wSize; i < N; i++) {
                sum = sum + cardPoints[i] - cardPoints[i - wSize];
                minSum = Math.min(minSum, sum);
            }
            return Arrays.stream(cardPoints).sum() - minSum;
        }
    }

29、绝对差不超过限制的最长连续

class Solution {
    public int longestSubarray(int[] nums, int limit) {
        Deque minQue = new ArrayDeque<>();
        Deque maxQue = new ArrayDeque<>();

        int left = 0;
        int right = 0;
        int n = nums.length;
        int res = 0;
        while (right < n) {
            int curVal = nums[right];
            while (!minQue.isEmpty() && minQue.peekLast() > curVal) {
                minQue.pollLast();
            }
            minQue.add(curVal);
            while (!maxQue.isEmpty() && maxQue.peekLast() < curVal) {
                maxQue.pollLast();
            }
            maxQue.add(curVal);

            // 左移
            while (!minQue.isEmpty() && !maxQue.isEmpty() && maxQue.peek() - minQue.peek() > limit) {
                if (nums[left] == minQue.peek()) {
                    minQue.poll();
                }
                if (nums[left] == maxQue.peek()) {
                    maxQue.poll();
                }
                left++;
            }
            right++;
            res = Math.max(res, right - left);
        }
        return res;
    }
}

30、删除子数组的最大得分

class Solution {
        public int maximumUniqueSubarray(int[] nums) {

            Set set = new HashSet<>();
            int maxSum = 0;
            int sum = 0;
            int left = 0;
            int right = 0;
            while (right < nums.length) {
                sum += nums[right];
                while (set.contains(nums[right])) {
                    set.remove(nums[left]);
                    sum -= nums[left++];
                }
                maxSum = Math.max(maxSum, sum);
                set.add(nums[right++]);
            }
            return maxSum;
        }
    }

31、跳跃游戏 VI,涉及动态规划,遗留

32、至多包含两个不同字符的最长子串

class Solution {
    // 用hashmap,维护窗口字符数量和个数
    public int lengthOfLongestSubstringTwoDistinct(String s) {
        int N = s.length();
        int res = 0;
        Map map = new HashMap<>();
        int left = 0;
        int right = 0;
        while (right < N) {
            char c = s.charAt(right);
            map.put(c, map.getOrDefault(c, 0) + 1);
            while (map.size() > 2) {
                char leftChar = s.charAt(left);
                map.put(leftChar, map.get(leftChar) - 1);
                if (map.get(leftChar) == 0) map.remove(leftChar);
                left++;
            }
            right++;
            res = Math.max(res, right - left);
        }
        return res;
    }
}

33、795. 区间子数组个数

// 计算结果时考虑需要增加的子数组的个数是多少
class Solution {
    public int numSubarrayBoundedMax(int[] nums, int left, int right) {
        int N = nums.length;
        int i = 0;
        int j = 0;
        int res = 0;
        int cnt = 0;
        while(j < N) {
            int cur = nums[j++];
            if (cur > right) {
                i = j;
                cnt = 0;
            } else if (cur < left){
                res += cnt;
            } else {
                res += j - i;
                cnt = j - i;     // 一开始写成了cnt++,忽略了一些情况。只要其中包含一个满足条件的最大数即可。
            }
        }
        return res;
    }
}

34、1358. 包含所有三种字符的子字符串数目

class Solution {
    // 自己想到的,每次加上以左边为起点的所有子字符串
    public int numberOfSubstrings(String s) {
        int N = s.length();
        int[] cnt = new int[3];
        int ans = 0;
        int left = 0;
        int right = 0;
        while (right < N) {
            cnt[s.charAt(right)-'a']++;
            while(cnt[0]>0 && cnt[1]>0 && cnt[2]>0) {
                ans += N - right;          // 此时如果满足,则加上后面的也满足。
                cnt[s.charAt(left++)-'a']--;
            }
            right++;
        }
        return ans;
    }
}

35、467. 环绕字符串中唯一的子字符串, dp解法

36、340. 至多包含 K 个不同字符的最长子串

// 典型滑动窗口题,一次过100%
class Solution {
    public int lengthOfLongestSubstringKDistinct(String s, int k) {
        int N = s.length();
        int[] cnt = new int[128];
        int left = 0;
        int right = 0;
        int sum = 0;
        int res = 0;
        while (right < N) {
            char cur = s.charAt(right++);
            if (cnt[cur]++ == 0) sum++;
            while (sum > k) {
                if (cnt[s.charAt(left++)]-- == 1) sum--;
            }
            res = Math.max(res, right - left);
        }
        return res;
    }
}

你可能感兴趣的:(刷题:sliding window)