LeetCode(395):至少有K个重复字符的最长子串 Longest Substring with At Least K Repeat Characters

2019.10.7 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新)

github:https://github.com/ChopinXBP/LeetCode-Babel

一般子串问题都可以用滑动窗口来解决,这题有两种方法:

1.递归分割子串

1.统计所有字母出现的频次

2.判断整个字符串是否满足最长子串要求,如果满足就直接返回字符串长度

3.将出现频次小于k的字符作为分割点(如果当前字母频次小于k,则一定不可能出现在当前子串中),递归判断所有被分割的子串。

2.动态规划+滑动窗口法

对目标字母个数numUniqueTarget从1迭代到26,求在numUniqueTarget个不同字母至少重复出现k次的情况下,最长满足子串的长度.

判断子串是否满足,需要借助滑动窗口法。

1.窗口拓展:窗口右侧end向前,统计出现过的字母个数numUnique(可能大于1次)和出现至少k次的字母个数numNoLessThanK。

2.窗口压缩:当出现过的字母个数numUnique超过目标字母个数numUniqueTarget时,窗口左侧begin向前,压缩窗口直到两者相同。

3.当窗口内出现过的字母个数numUnique、目标字母个数numUniqueTarget、出现至少k次的字母个数numNoLessThanK相等时,说明窗口子串满足条件,记录长度。


传送门:至少有K个重复字符的最长子串

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.

找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k 。输出 T 的长度。

示例 1:
输入:
s = "aaabb", k = 3
输出:
3
最长子串为 "aaa" ,其中 'a' 重复了 3 次。

示例 2:
输入:
s = "ababbc", k = 2
输出:
5
最长子串为 "ababb" ,其中 'a' 重复了 2 次, 'b' 重复了 3 次。


/**
 *
 * 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.
 * 找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k 。输出 T 的长度。
 *
 */

public class LongestSubstringWithAtLeastKRepeatingCharacters {
    //递归分割子串
    public int longestSubstring(String s, int k) {
        if(s == null || s.length() == 0){
            return 0;
        }

        //统计所有字母出现的频次
        int[] chars = new int[26];
        for(int i = 0; i < s.length(); i++){
            chars[s.charAt(i) - 'a']++;
        }

        //判断整个字符串是否满足最长子串要求,如果满足就直接返回字符串长度
        boolean flag = true;
        for(int nums : chars){
            if(nums > 0 && nums < k){
                flag = false;
            }
        }
        if(flag){
            return s.length();
        }

        //将出现频次小于k的字符作为分割点,递归判断所有被分割的子串
        int result = 0;
        int start = 0;
        int cur = 0;
        while(cur < s.length()){
            //如果当前字母频次小于k,则一定不可能出现在当前子串中,将其分割。
            //递归判断cur之前start-cur的子串是否满足,再将start从cur之后开始
            if(chars[s.charAt(cur) - 'a'] < k){
                result = Math.max(result, longestSubstring(s.substring(start, cur), k));
                start = cur + 1;
            }
            cur++;
        }
        result = Math.max(result, longestSubstring(s.substring(start), k));

        return result;
    }

    //动态规划+滑动窗口法
    public int longestSubstring2(String s, int k) {
        int result = 0;
        for(int i = 1; i < 26; i++){
            result = Math.max(result, Solution(s, k, i));
        }
        return result;
    }

    //求在numUniqueTarget个不同字母至少重复出现k次的情况下,最长满足子串的长度
    private int Solution(String s, int k, int numUniqueTarget){
        int[] words = new int[26];
        int numUnique = 0;
        int numNoLessThanK = 0;

        int begin = 0;
        int end = 0;
        int result = 0;
        while(end < s.length()){
            //窗口右侧end向前,统计出现过的字母个数numUnique(可能大于1次)和出现至少k次的字母个数numNoLessThanK(用==避免重复计算)
            if(words[s.charAt(end) - 'a']++ == 0){
                numUnique++;
            }
            if(words[s.charAt(end++) - 'a'] == k){
                numNoLessThanK++;
            }
            //当出现过的字母个数numUnique超过目标字母个数numUniqueTarget时,压缩窗口直到两者相同
            while(numUnique > numUniqueTarget){
                if(words[s.charAt(begin) - 'a']-- == k){
                    numNoLessThanK--;
                }
                if(words[s.charAt(begin++) - 'a'] == 0){
                    numUnique--;
                }
            }
            //当窗口内出现过的字母个数numUnique、目标字母个数numUniqueTarget、出现至少k次的字母个数numNoLessThanK相等时,说明窗口子串满足条件
            if(numUnique == numUniqueTarget && numUnique == numNoLessThanK){
                result = Math.max(end - begin, result);
            }
        }

        return result;
    }
}




#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

你可能感兴趣的:(JAVA,LeetCode,数据结构与算法)