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

https://leetcode.com/problems/longest-substring-with-at-least-k-repeating-characters/discuss/87738/Java-20-lines-very-easy-solution-7ms-with-explanation

 

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

究极缩写版

posted @ 2020-06-20 08:07  Schwifty  阅读(193)  评论(0编辑  收藏  举报