每日一题 395. 至少有 K 个重复字符的最长子串

437 阅读1分钟

395. 至少有 K 个重复字符的最长子串

1 题目描述

输入一个字符串s,以及数字k,要求返回最长子串长度,字串要求其中的每个字符的频数超过k。

注意:字符串中的字符只包含所有小写字母

2 解题思路

2.1 分治

对字符串当中的每个小写字母计数

  • 如果字符串所有的字母频数都超过k,当前串就是要求的最长子串
  • 如果字符串中存在某个小写字母,但这个小写字母的个数小于k,那么说明当前字符串不是要求的最长字串,所有包含了这个小写字母的串都不可能满足条件,我们需要把这个小写字母剔除,将当前串分成若干个子串
    • 对每个子串进行相同操作求解子串的最长子串长度
    • 返回子串中最长子串长度的最大值

时间复杂度 O(n* m)

1、对当前字符串每个字母计数 O(n)
2、寻找应当被剔除的字母 O(26)
3、划分子字符串O(n) 4、递归层数最多为m,m代表字符类型个数,每层子串个数加起来不超过n

class Solution {
    public int longestSubstring(String s, int k) {
        int n = s.length();

        return dfs(s,k,0,n - 1);
    }

    public int dfs(String s, int k,int left,int right){
        int[] count = new int[26];
        for(int i = left; i <= right; i ++){
            count[s.charAt(i) - 'a'] += 1;
        }

        char split = 0;
        for(int i = 0; i < 26 ; i++){
            if(count[i] > 0 && count[i] < k){
                split = (char)(i + 'a');
                break;
            }
        }

        if(split == 0){
            return right - left + 1;
        }

        int max = 0;
        int i = left;
        int start = 0;
        int end = 0;
        while(i <= right){
            while(i <= right && s.charAt(i) == split){
                i ++;
            }

            if(i > right){
                break;
            }

            start = i;

            while( i <= right && s.charAt(i) != split){
                i ++;
            }
            end = i - 1;

            int maxForCurrent = dfs(s,k,start,end);
            max = Math.max(maxForCurrent,max);
        }

        return max;
    }
}