395. Longest Substring with At Least K Repeating Characters
Find the length of the longest substring T of a given string (consists of lowercase letters only) such that every character in T appears no less than k times.
Example 1:
Input: s = "aaabb", k = 3 Output: 3 The longest substring is "aaa", as 'a' is repeated 3 times.
Example 2:
Input: s = "ababbc", k = 2 Output: 5 The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times.
class Solution { public int longestSubstring(String s, int k) { int res = 0; for(int i = 0; i <= s.length() - k; i++){ for(int j = i + k; j <= s.length(); j++){ String t = s.substring(i, j); if(help(t, k)){ res = Math.max(res, j - i); } } } return res; } public boolean help(String s, int k){ int[] arr = new int[128]; for(char c: s.toCharArray()) arr[c]++; for(int i: arr){ if(i != 0 && i < k) return false; } return true; } }
1. brute force, TLE
class Solution { public int longestSubstring(String s, int k) { if (s == null || s.length() == 0) return 0; if (k<2) return s.length(); return helper(s, 0, s.length(), k); } public int helper(String s, int l, int r, int k) { if (l>=r) return 0; // build freq map int[] freq = new int[26]; for (int i=l; i<r; i++) freq[s.charAt(i)-'a']++; // check if valid boolean valid = true; for (int i=0; i<26 && valid; i++) if (freq[i] > 0 && freq[i] < k) valid = false; if (valid) return r-l; // if not for each invalid character start a new split search int best = 0, start=l; for (int i=l; i<r; i++) { if (freq[s.charAt(i) -'a'] < k) { best = Math.max(best, helper(s, start, i, k)); start = i+1; } } best = Math.max(best, helper(s, start, r, k)); return best; } }
public class Solution { /** * Given a String s and an integer k, return the longest "valid" substring, * where a substring is valid iff every character in the substring occurs * at least k times. * * @param s The given String * @param k The minimum number of times all substring characters must occur * @return The length of the longest valid substring */ public int longestSubstring(String s, int k) { // Call divide and conquer helper method return div(s, 0, s.length(), k); } /** * Determines the length of the longest valid substring. * * We achieve this by recursively splitting the given String on characters * who do not occur at least k times (since they cannot be part of the * longest valid substring). * * Note that the substring of the current recursion is always equivalent * to s.substring(start, end). For space reasons, we don't ever actually * create a new substring. * * @param s The given String * @param start The beginning of the substring, inclusive * @param end The end of the substring, exclusive * @param k The minimum number of times all substring characters must occur * @return The length of the longest valid substring */ private int div(String s, int start, int end, int k) { /** * Base Case 1 of 2: * * If this substring is shorter than k, then no characters in it * can be repeated k times, therefore this substring and all * substrings that could be formed from it are invalid, * therefore return 0. */ if (end - start < k) return 0; /** * Count the frequency of characters in this substring. * * We are guaranteed from the problem statement that the given String * can only contain lowercase (English?) characters, so we use a * table of length 26 to store the character counts. */ int[] a = new int[26]; for (int i = start; i < end; i++) { a[s.charAt(i)-'a']++; } // For every character in the above frequency table for (int i = 0; i < a.length; i++){ /** * If this character occurs at least once, but fewer than k times * in this substring, we know: * (1) this character cannot be part of the longest valid substring, * (2) the current substring is not valid. * * Hence, we will "split" this substring on this character, * wherever it occurs, and check the substrings formed by that split */ if (a[i] > 0 && a[i] < k) { /** * Look for each occurrence of this character (i + 'a') * in this substring. */ for (int j = start; j < end; j++) { if (s.charAt(j) == i + 'a') { // "Split" into two substrings to solve recursively int l = div(s, start, j, k); int r = div(s, j + 1, end, k); return Math.max(l, r); } } } } /** * Base Case 2 of 2: * * If every character in this substring occurs at least k times, * then this is a valid substring, so return this substring's length. */ return end - start; } }
原理就是divide and conquer,如果这个字母的frequency小于k,说明这个字母铁废物,包括他就不能成为合格的substring,那就往左右两边看,递归看是否有合适的可能出现。
class Solution { public int longestSubstring(String s, int k) { return help(s, 0, s.length(), k); } public int help(String s, int start, int end, int k) { if(end - start < k) return 0; int[] count = new int[26]; for(int i = start; i < end; i++) count[s.charAt(i) - 'a']++; for(int i = start; i < end; i++) { if(count[s.charAt(i) - 'a'] < k) { return Math.max(help(s, start, i, k), help(s, i + 1, end, k)); } } return end - start; } }
究极缩写版