---
# System prepended metadata

title: 0395. Longest Substring with At Least K Repeating Characters
tags: [Leetcode, Divide and Conquer, Sliding Window, Medium]

---

# 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;
    }
}
```