《滑动窗口》不定长滑动窗口(求最长/最大)

1. LeetCode 3 无重复字符的最长子串

方法1:滑动窗口 + 哈希

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashSet<Character> set = new HashSet<Character>();
        int left = 0, ans = 0;
        for (int right = 0; right < s.length(); right++) {
            while (set.contains(s.charAt(right)))
                set.remove(s.charAt(left++));
            ans = Math.max(ans, right - left + 1);
            set.add(s.charAt(right));
        }
        return ans;
    }
}
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        st = set(); left = ans = 0
        for right, ch in enumerate(s):
            while ch in st:
                st.remove(s[left])
                left += 1
            ans = max(ans, right - left + 1)
            st.add(ch)
        return ans

2. LeetCode 1493 删掉一个元素以后全为1的最长子数组

方法1:滑动窗口

class Solution {
    public int longestSubarray(int[] nums) {
        int left = 0, ans = 0, cnt = 0; // 标记当前 0 的个数
        for (int right = 0; right < nums.length; right++) {
            if (nums[right] == 0)
                cnt += 1;
            while (cnt > 1) {
                if (nums[left] == 0)
                    cnt -= 1;
                left += 1;
            }
            // 保证[left , right]中只有一个 0
            ans = Math.max(ans, right - left + 1);
        }
        return ans - 1;
    }
}
class Solution:
    def longestSubarray(self, nums: List[int]) -> int:
        cnt, left, ans = 0, 0, 0
        for right, x in enumerate(nums):
            if x == 0:
                cnt += 1
            while cnt > 1:
                if nums[left] == 0:
                    cnt -= 1
                left += 1
            # 保证[left, right]中只有一个 0
            ans = max(ans, right - left + 1)
        return ans - 1 # 减去其中需要删除的一个元素

方法2:思维 + 前缀和

class Solution {
    public int longestSubarray(int[] nums) {
        int n = nums.length;
        
        int[] prev = new int[n]; // 当前位置结尾前面连续 1 的个数
        prev[0] = nums[0]; // 初始化
        for (int i = 1; i < n; i++)
            prev[i] = nums[i] == 1 ? prev[i - 1] + 1 : 0;
        
        int[] next = new int[n]; // 当前位置结尾后面连续 1 的个数
        next[n - 1] = nums[n - 1]; // 初始化
        for (int i = n - 2; i >= 0; i--)
            next[i] = nums[i] == 1 ? next[i + 1] + 1 : 0;
        
        int ans = 0;
        for (int i = 0; i < n; i++) { // 枚举要删除的位置
            int prevS = i == 0 ? 0 : prev[i - 1];
            int nextS = i == n - 1 ? 0 : next[i + 1];    
            ans = Math.max(ans, prevS + nextS);
        }
        return ans;
    }
}
class Solution:
    def longestSubarray(self, nums: List[int]) -> int:
        n = len(nums)
        pre = [nums[0]] * n; nxt = [nums[n - 1]] * n
        for i in range(1, n): # 初始化 pre 数组
            pre[i] = 0 if nums[i] == 0 else pre[i - 1] + 1
        for i in range(n - 2, -1, -1):
            nxt[i] = 0 if nums[i] == 0 else nxt[i + 1] + 1
        ans = 0
        for i in range(n):
            preS = 0 if i == 0 else pre[i - 1]
            nxtS = 0 if i == n - 1 else nxt[i + 1]
            ans = max(ans, preS + nxtS)
        return ans

3. LeetCode 2730 找到最长的半重复子字符串

方法1:滑动窗口

class Solution {
    public int longestSemiRepetitiveSubstring(String s) {
        int val = 0, left = 0, ans = 1; // 当只有一个字符时返回 1
        for (int right = 1; right < s.length(); right++) {
            if (s.charAt(right) == s.charAt(right - 1))
                val += 1; // 相邻重复个数加 1
            while (val > 1) {
                if(s.charAt(left) == s.charAt(left + 1))
                    val -= 1;
                left += 1;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}
class Solution:
    def longestSemiRepetitiveSubstring(self, s: str) -> int:
        cnt, left, ans = 0, 0, 1
        for right in range(1, len(s)):
            if s[right - 1] == s[right]:
                cnt += 1
            while cnt > 1:
                if s[left] == s[left + 1]:
                    cnt -= 1
                left += 1
            ans = max(ans, right - left + 1)
        return ans

4. LeetCode 904 水果成篮

方法1:滑动窗口 + 哈希表

class Solution {
    public int totalFruit(int[] fruits) {
        // 标记区间数字出现次数
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        // left:左端点 ans:最终答案
        int left = 0, ans = 0;
        for (int right = 0; right < fruits.length; right++) {
            map.merge(fruits[right], 1, Integer::sum);
            while (map.size() > 2) {
                if (map.merge(fruits[left], -1, Integer::sum) == 0)
                    map.remove(fruits[left]);                
                left += 1;
            }
            ans = Math.max(ans, right - left + 1);
        }   
        return ans;
    }
}
class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        cnt = Counter() # python的计数器
        left, ans = 0, 0
        for right, x in enumerate(fruits):
            cnt[x] += 1 # 如果不存在会自动创建
            while len(cnt) > 2:
                cnt[fruits[left]] -= 1
                if cnt[fruits[left]] == 0: # 需要手动删除
                    cnt.pop(fruits[left])
                left += 1
            ans = max(ans, right - left + 1)
        return ans

5. LeetCode 1695 删除子数组的最大得分

方法1:滑动窗口 + 哈希表

class Solution {
    public int maximumUniqueSubarray(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        int left = 0, val = 0, ans = 0;
        for (int right = 0; right < nums.length; right++) {
            map.merge(nums[right], 1, Integer::sum);
            val += nums[right];
            while (map.get(nums[right]) > 1) {
                map.merge(nums[left], -1, Integer::sum);
                // 此处可以删除键值为 0 的键
                val -= nums[left]; left++;
            }
            ans = Math.max(ans, val);
        }
        return ans;
    }
}
class Solution:
    def maximumUniqueSubarray(self, nums: List[int]) -> int:
        cnt = Counter() # 统计每个数字出现的次数
        left, val, ans = 0, 0, 0
        for right, x in enumerate(nums):
            cnt[x] += 1; val += x
            while cnt[x] > 1:
                cnt[nums[left]] -= 1
                val -= nums[left]
                # 此处可以删除个数为 0 的键
                left += 1
            ans = max(ans, val)
        return ans

6. LeetCode 2958 最多 K 个重复元素的最长子数组

方法1:滑动窗口 + 哈希表

class Solution {
    public int maxSubarrayLength(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        int left = 0, ans = 0;
        for (int right = 0; right < nums.length; right++) {
            map.merge(nums[right], 1, Integer::sum);
            while (map.get(nums[right]) > k) {
                map.merge(nums[left], -1, Integer::sum);
                // 此处可以删除键值为 0 的键
                left++;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}
class Solution:
    def maxSubarrayLength(self, nums: List[int], k: int) -> int:
        cnt = Counter() # 统计每个数字出现的次数
        left, ans = 0, 0
        for right, x in enumerate(nums):
            cnt[x] += 1
            while cnt[x] > k:
                cnt[nums[left]] -= 1
                # 此处可以删除个数为 0 的键
                left += 1
            ans = max(ans, right - left + 1)
        return ans

7. LeetCode 1004 最大连续1的个数III

方法1:滑动窗口

class Solution {
    public int longestOnes(int[] nums, int k) {
        int cnt = 0, left = 0, ans = 0;
        for (int right = 0; right < nums.length; right++) {
            cnt += nums[right] == 0 ? 1 : 0;
            while (cnt > k) {
                cnt -= nums[left] == 0 ? 1 : 0;
                left += 1;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}
class Solution:
    def longestOnes(self, nums: List[int], k: int) -> int:
        cnt, left, ans = 0, 0, 0
        for rigth, x in enumerate(nums):
            cnt += 1 if x == 0 else 0
            while cnt > k:
                cnt -= 1 if nums[left] == 0 else 0
                left += 1
            ans = max(ans, rigth - left + 1)
        return ans

8. LeetCode 2024 考试的最大困扰度

方法1:滑动窗口

class Solution {
    public int maxConsecutiveAnswers(String answerKey, int k) {
        return Math.max(longest(answerKey, k, 'T'), longest(answerKey, k, 'F'));
    }

    public int longest(String s, int k, char ch) {
        int cnt = 0, left = 0, ans = 0;
        for (int right = 0; right < s.length(); right++) {
            cnt += s.charAt(right) == ch ? 1 : 0;
            while (cnt > k) {
                cnt -= s.charAt(left) == ch ? 1 : 0;
                left += 1;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}
class Solution:
    def maxConsecutiveAnswers(self, answerKey: str, k: int) -> int:
        return max(self.longest(answerKey, k, 'T'), self.longest(answerKey, k, 'F'))

    def longest(self, s: str, k: int, ch: str) -> int:
        cnt, left, ans = 0, 0, 0
        for rigth, x in enumerate(s):
            cnt += 1 if x == ch else 0
            while cnt > k:
                cnt -= 1 if s[left] == ch else 0
                left += 1
            ans = max(ans, rigth - left + 1)
        return ans

9. LeetCode 1438 绝对差不超过限制的最长连续子数组

方法1:滑动窗口 + 有序集合

class Solution {
    public int longestSubarray(int[] nums, int limit) {
        // 有序红黑树
        TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
        int n = nums.length, left = 0, ans = 0;
        for (int right = 0; right < n; right++) {
            map.merge(nums[right], 1, Integer::sum); // 将当前值放入表中
            while (map.lastKey() - map.firstKey() > limit) {
                // 需要依次删除左边元素 保证符合要求
                if (map.merge(nums[left], -1, Integer::sum) == 0)
                    map.remove(nums[left]);
                left += 1;
            }
            ans = Math.max(ans, right - left + 1); // 记录
        }
        return ans;
    }
}
class Solution:
    def longestSubarray(self, nums: List[int], limit: int) -> int:
        from sortedcontainers import SortedList  # Python的有序集合
        s, left, ans = SortedList(), 0, 0
        for right in range(len(nums)):
            s.add(nums[right])
            while s[-1] - s[0] > limit:
                s.remove(nums[left])
                left += 1
            ans = max(ans, right - left + 1)
        return ans

方法2:滑动窗口 + 单调队列

class Solution {
    public int longestSubarray(int[] nums, int limit) {
        // 使用单调队列保存当前窗口的最大值和最小值  
        ArrayDeque<Integer> Max = new ArrayDeque<Integer>();
        ArrayDeque<Integer> Min = new ArrayDeque<Integer>();
        int n = nums.length, left = 0, ans = 0;
        for (int right = 0; right < n; right++) {
            // 1.保持单调队列的单调性
            while (!Max.isEmpty() && Max.peekLast() < nums[right])
                Max.pollLast(); // 单调递减 队头为最大值
            while (!Min.isEmpty() && Min.peekLast() > nums[right])
                Min.pollLast(); // 单调递增 队头为最小值
            // 2.添加元素
            Max.offerLast(nums[right]); Min.offerLast(nums[right]);
            // 3.保证当前窗口满足要求
            while (!Max.isEmpty() && !Min.isEmpty() && Max.peekFirst() - Min.peekFirst() > limit) {
                // 依次删除左边的值
                if (nums[left] == Min.peekFirst())
                    Min.pollFirst();
                if (nums[left] == Max.peekFirst())
                    Max.pollFirst();
                left += 1;
            }
            // 4.记录答案
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}
class Solution:
    def longestSubarray(self, nums: List[int], limit: int) -> int:
        from collections import deque
        Max, Min = deque(), deque()
        left, ans = 0, 0
        for right in range(len(nums)):
            while Max and Max[-1] < nums[right]:
                Max.pop()
            while Min and Min[-1] > nums[right]:
                Min.pop()
            Max.append(nums[right]); Min.append(nums[right])
            while Max and Min and Max[0] - Min[0] > limit:
                if Max[0] == nums[left]:
                    Max.popleft()
                if Min[0] == nums[left]:
                    Min.popleft()
                left += 1
            ans = max(ans, right - left + 1)
        return ans

10. LeetCode 2401 最长优雅子数组

方法1:滑动窗口 + 集合论

class Solution {
    public int longestNiceSubarray(int[] nums) {
        int n = nums.length, left = 0, val = 0, ans = 1;
        for (int right = 0; right < n; right++) {
            while ((val & nums[right]) > 0) // 需要移动 left 保证其为0
                val ^= nums[left++];
            val |= nums[right];
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}
class Solution:
    def longestNiceSubarray(self, nums: List[int]) -> int:
        left, val, ans = 0, 0, 1
        for right in range(len(nums)):
            while val & nums[right]:
                val ^= nums[left]
                left += 1
            val |= nums[right]
            ans = max(ans, right - left + 1)
        return ans

11. LeetCode 1658 将x减到0的最小操作数

方法1:滑动窗口 + 前缀和

class Solution {
    public int minOperations(int[] nums, int x) {
        int n = nums.length;
        int s = Arrays.stream(nums).sum(); // 流
        // 某个前缀 + 某个后缀 == x 且 长度最小
        if (s < x) return -1;
        int right = 0, ans = n + 1; // ans求最小
        int lsum = 0, rsum = s;
        for (int left = -1; left < n; left++) {
            // (1)lsum + rsum < x 将left左移
            lsum += left == -1 ? 0 : nums[left];
            // (2)lsum + rsum > x 将right左移
            while (right < n && lsum + rsum > x) 
                rsum -= nums[right++];
            // (3)lsum + rsum == x 记录答案
            if (lsum + rsum == x)
                ans = Math.min(ans, left + 1 + n - right);
        }
        return ans == n + 1 ? -1 : ans;
    }
}
class Solution:
    def minOperations(self, nums: List[int], x: int) -> int:
        n = len(nums)
        right, ans = 0, n + 1
        lsum, rsum = 0, sum(nums)
        for left in range(-1, n):
            lsum += 0 if left == -1 else nums[left]
            while right < n and lsum + rsum > x:
                rsum -= nums[right]
                right += 1
            if lsum + rsum == x:
                ans = min(ans, left + 1 + n - right)
        return -1 if ans == n + 1 else ans

方法2:滑动窗口 + 逆向思维

class Solution {
    public int minOperations(int[] nums, int x) {
        int n = nums.length;
        int s = Arrays.stream(nums).sum();
        // 使和为 s - x 的区间长度最长
        if (s < x) return -1;
        if (s == x) return n; // 可以保证后面必有元素被删除
        int left = 0, val = 0, ans = 0;
        for (int right = 0; right < n; right++) {
            val += nums[right];
            while (val > s - x)
                val -= nums[left++];
            if (val == s - x)
                ans = Math.max(ans, right - left + 1);
        }
        return ans == 0 ? -1 : n - ans;
    }
}
class Solution:
    def minOperations(self, nums: List[int], x: int) -> int:
        s = sum(nums); n = len(nums)
        if s < x: return -1
        if s == x: return n
        left = 0; val = 0; ans = 0
        for right in range(n):
            val += nums[right]
            while val > s - x:
                val -= nums[left]
                left += 1
            if val == s - x:
                ans = max(ans, right - left + 1)
        return -1 if ans == 0 else n - ans

12. LeetCode 1838 最高频元素的频数

方法1:滑动窗口 + 思维 + 排序

class Solution {
    public int maxFrequency(int[] nums, int k) {
        Arrays.sort(nums); // 前提是数组有序
        int n = nums.length;
        long cnt = 0; // 防止溢出
        int left = 0, ans = 1;
        for (int right = 1; right < n; right++) {
            // 依次放入每一个数并计算所需操作数 将第一个数转为long
            cnt += (long)(nums[right] - nums[right - 1]) * (right - left);
            while (cnt > k) { // 移动left减少操作数
                cnt -= nums[right] - nums[left];
                left += 1;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}
class Solution:
    def maxFrequency(self, nums: List[int], k: int) -> int:
        nums.sort()
        cnt, left, ans = 0, 0, 1
        for right in range(1, len(nums)):
            cnt += (right - left) * (nums[right] - nums[right - 1])
            while cnt > k:
                cnt -= nums[right] - nums[left]
                left += 1
            ans = max(ans, right - left + 1) 
        return ans

13. LeetCode 2516 每种字符至少取K个

14. LeetCode 2831 找出最长等值子数组

15. LeetCode 2106 摘水果

16. LeetCode 1610 可见点的最大数目

17. LeetCode 2781 最长合法子字符串的长度

18. LeetCode 2968 执行操作使频率分数最大

19. LeetCode 395 至少有K个重复字符的最长子串

20. LeetCode 1763 最长的美好子字符串

posted @   Koonan-Edogawa  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示