import java.util.*; /** * NC364 至少有 K 个重复字符的最长子串 * @author d3y1 */ public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param s string字符串 * @param k int整型 * @return int整型 */ public int longestSubstring (String s, int k) { // return solution1(s, k); // return solution2(s, k); // return solution22(s, k); // return solution3(s, k); return solution33(s, k); } /** * 滑动窗口 * @param s * @param k * @return */ private int solution1(String s, int k){ int len = s.length(); if(k == 1){ return len; } if(len < k){ return 0; } // 可能的最大窗口 int max = 0; int[] cnt = new int[26]; for(char ch: s.toCharArray()){ cnt[ch-'a']++; } for(int i=0; i<26; i++){ if(cnt[i] >= k){ max += cnt[i]; } } // 滑动窗口 for(int gap=max; gap>=k; gap--){ for(int i=0; i+gap<=len; i++){ if(isValid(s.substring(i, i+gap), k)){ return gap; } } } return 0; } /** * 子串是否合法 * @param sub * @param k * @return */ private boolean isValid(String sub, int k){ int[] cnt = new int[26]; for(char ch: sub.toCharArray()){ cnt[ch-'a']++; } for(int i=0; i<26; i++){ if(0<cnt[i] && cnt[i]<k){ return false; } } return true; } /** * 分治法 * @param s * @param k * @return */ private int solution2(String s, int k){ return divide(s, k); } /** * 递归 * @param s * @param k * @return */ private int divide(String s, int k){ int len = s.length(); if(k == 1){ return len; } if(len < k){ return 0; } // 哈希 HashMap<Character, Integer> map = new HashMap<>(); for(char ch: s.toCharArray()){ map.put(ch, map.getOrDefault(ch, 0)+1); } for(char ch: s.toCharArray()){ if(map.get(ch) < k){ int maxLen = 0; for(String part: s.split(String.valueOf(ch))){ maxLen = Math.max(maxLen, divide(part, k)); } return maxLen; } } return s.length(); } /** * 分治法 * @param s * @param k * @return */ private int solution22(String s, int k){ return dfs(s, k); } /** * 递归 * @param s * @param k * @return */ private int dfs(String s, int k){ int len = s.length(); if(k == 1){ return len; } if(len < k){ return 0; } // 哈希 int[] cnt = new int[26]; for(char ch: s.toCharArray()){ cnt[ch-'a']++; } for(char ch: s.toCharArray()){ if(cnt[ch-'a'] < k){ int maxLen = 0; for(String part: s.split(String.valueOf(ch))){ maxLen = Math.max(maxLen, divide(part, k)); } return maxLen; } } return s.length(); } /** * 双指针 * * 相似 -> NC402 包含不超过两种字符的最长子串 [nowcoder] * 相似 -> NC41 最长无重复子数组 [nowcoder] * 相似 -> NC170 最长不含重复字符的子字符串 [nowcoder] * 相似 -> NC356 至多包含K种字符的子串 [nowcoder] * 相似 -> NC387 找到字符串中的异位词 [nowcoder] * * @param s * @param k * @return */ private int solution3(String s, int k){ int n = s.length(); int result = 0; // 枚举最长子串的字符种数 for(int kind=1; kind<=26; kind++){ // 双指针 毛毛虫 int left=0, right=0; // 滑动窗口内每种字符出现的次数统计 int[] cnt = new int[26]; // 滑动窗口内的字符种数 int total = 0; // 滑动窗口内出现次数小于k的字符种数 int remain = 0; char chL,chR; while(right < n){ chR = s.charAt(right); cnt[chR-'a']++; if(cnt[chR-'a'] == 1){ total++; remain++; } if(cnt[chR-'a'] == k){ remain--; } while(total > kind){ chL = s.charAt(left); cnt[chL-'a']--; if(cnt[chL-'a'] == k-1){ remain++; } if(cnt[chL-'a'] == 0){ total--; remain--; } left++; } if(remain == 0){ result = Math.max(result, right-left+1); } right++; } } return result; } /** * 双指针 * * 相似 -> NC402 包含不超过两种字符的最长子串 [nowcoder] * 相似 -> NC41 最长无重复子数组 [nowcoder] * 相似 -> NC170 最长不含重复字符的子字符串 [nowcoder] * 相似 -> NC356 至多包含K种字符的子串 [nowcoder] * 相似 -> NC387 找到字符串中的异位词 [nowcoder] * * @param s * @param k * @return */ private int solution33(String s, int k){ int n = s.length(); int result = 0; HashSet<Character> set = new HashSet<>(); for(char ch: s.toCharArray()){ set.add(ch); } // 枚举最长子串的字符种数 for(int kind=1; kind<=set.size(); kind++){ // 双指针 毛毛虫 int left=0, right=0; // 滑动窗口内每种字符出现的次数统计 int[] cnt = new int[26]; // 滑动窗口内的字符种数 int total = 0; // 滑动窗口内出现次数小于k的字符种数 int remain = 0; char chL,chR; while(right < n){ chR = s.charAt(right); cnt[chR-'a']++; if(cnt[chR-'a'] == 1){ total++; remain++; } if(cnt[chR-'a'] == k){ remain--; } while(total > kind){ chL = s.charAt(left); cnt[chL-'a']--; if(cnt[chL-'a'] == k-1){ remain++; } if(cnt[chL-'a'] == 0){ total--; remain--; } left++; } if(remain == 0){ result = Math.max(result, right-left+1); } right++; } } return result; } }