# 0395. Longest Substring with At Least K Repeating Characters ###### tags: `Leetcode` `Medium` `Divide and Conquer` `Sliding Window` Link: https://leetcode.com/problems/longest-substring-with-at-least-k-repeating-characters/description/ ## 思路 ### Divide and Conquer $O(N^2)$ $O(N)$ 和[1763. Longest Nice Substring](https://hackmd.io/cIMdRw03R_iRQtFsbS7kNA)有点像 对于那些出现次数少于k的字符,是“害群之马”,它们放在任何一个子序列中都会违反题意的。所以一个直观的想法是,将那些“害群之马”作为splitor,将原序列分割成若干子序列,然后递归调用函数本身,找到最长的有效子序列。递归的边界是,如果整个序列所有的字符频次都大于等于k,就可以返回序列的长度;如果整个序列的总长度都小于k,那么就返回零 ### Sliding Window $O(26N)$ $O(N)$ 这道题和别的sliding window不一样的地方在于这道题是一个固定window中unique character number的滑窗 只要固定了左指针和区间不同字母的个数,那么我们就可以确定右指针最远的位置,然后查看区间内是否每个字母出现的频次都大于k 可以想见,左指针的移动,必然也会导致右指针的单向移动 ## Code ### Divide and Conquer ```java= class Solution { public int longestSubstring(String s, int k) { int[] cnt = new int[26]; int n = s.length(); for(int i=0; i<n; i++) cnt[s.charAt(i)-'a']++; int ans = 0; int prev = 0; for(int i=0; i<n; i++){ if(cnt[s.charAt(i)-'a']<k){ ans = Math.max(ans, longestSubstring(s.substring(prev, i), k)); prev = i+1; } } if(prev==0) return n; else ans = Math.max(ans, longestSubstring(s.substring(prev), k)); return ans; } } ``` ### Sliding Window ```java= class Solution { public int longestSubstring(String s, int k) { int[] countMap = new int[26]; int maxUnique = getMaxUnique(s); int ans = 0; for(int i=1; i<=maxUnique; i++){ Arrays.fill(countMap, 0); int l = 0, r = 0, uni = 0, countAtLeastK = 0; while(r<s.length()){ if(countMap[s.charAt(r)-'a']==0) uni++; countMap[s.charAt(r)-'a']++; if(countMap[s.charAt(r)-'a']==k) countAtLeastK++; while(uni>i){ if(countMap[s.charAt(l)-'a']==k) countAtLeastK--; countMap[s.charAt(l)-'a']--; if(countMap[s.charAt(l)-'a']==0) uni--; l++; } if(uni==i && countAtLeastK==uni){ ans = Math.max(ans, r-l+1); } r++; } } return ans; } private int getMaxUnique(String s){ int[] cnt = new int[26]; int uni = 0; for(int i=0; i<s.length(); i++){ if(cnt[s.charAt(i)-'a']==0) uni++; cnt[s.charAt(i)-'a']++; } return uni; } } ```