力扣解题思路:395. 至少有K个重复字符的最长子串

本文介绍了如何解决LeetCode上的一个问题,即找到至少包含K个重复字符的最长子串。初始尝试使用双指针方法失败后,作者发现需要采用递归策略。当遇到不满足条件的字符时,将字符串分为两段并递归检查,确保所有相同字符的频率都在K以上。
摘要由CSDN通过智能技术生成

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

思路:在这里插入图片描述
看到这种题,第一反应是使用双指针,但是只遍历一趟显然是得不出结果的。

首先,如果使用双指针分别从头和尾部遍历,一旦头部或者尾部出现了不满足条件(出现次数小于k)的字母,两个指针就会移动。当两个指针停止移动时不代表中间的字符串都满足要求,这时开始遍历中的的字符串。

那么一旦中间又出现不满足条件(出现次数小于k)的字母,这两个指针间的字符串就不再满足条件了!所以,我们可以以这个不满足条件的字符为分界点,将这个字符串分成两段,再次验证这两段是否满足条件,也就是采用递归的方式

于是有:

public int longestSubstring(String s, int k) {
    int len = s.length();
    if (len == 0 || k > len) return 0;
    if (k < 2) return len;
    return count(s.toCharArray(), k, 0, len - 1);
}
private int count(char[] chars, int k, int p1, int p2) {//包含p1,p2
    if (p2 - p1 + 1 < k) return 0;
    int[] times = new int[26];  //  26个字母
    //  统计出现频次
    for (int i = p1; i <= p2; ++i) {
        ++times[chars[i] - 'a'];
    }
    //  如果该字符出现频次小于k,则不可能出现在结果子串中
    //  分别排除,然后挪动两个指针
    while (p2 - p1 + 1 >= k && times[chars[p1] - 'a'] < k) {
        ++p1;
    }
    while (p2 - p1 + 1 >= k && times[chars[p2] - 'a'] < k) {
        --p2;
    }
    if (p2 - p1 + 1 < k) return 0;
    //  得到临时子串,再递归处理
    for (int i = p1; i <= p2; ++i) {
        //  如果第i个不符合要求,切分成左右两段分别递归求得
        if (times[chars[i] - 'a'] < k) {
            return Math.max(i - p1, count(chars, k, i + 1, p2));//错误
        }
    }
    return p2 - p1 + 1;
}

然后就。。报错了,问题就在 return Math.max(i - p1, count(chars, k, i + 1, p2)); 这句,我以为从p1到i这段已经被验证过了所以无需再次遍历可以直接得出结果,但是!这样是存在漏洞的,比如虽然p1到i这段出现过的字符都是频率大于或者等于k的,但是未必他的所有相同字符都出现在这段被截断过的p1到i段(因为times是统计p1到p2这段得出,而不是p1到i段!),所以还需要再次验证(再次递归)。

正确代码:

public int longestSubstring(String s, int k) {
    int len = s.length();
    if (len == 0 || k > len) return 0;
    if (k < 2) return len;
    return count(s.toCharArray(), k, 0, len - 1);
}
private int count(char[] chars, int k, int p1, int p2) {//包含p1,p2
    if (p2 - p1 + 1 < k) return 0;
    int[] times = new int[26];  //  26个字母
    //  统计出现频次
    for (int i = p1; i <= p2; ++i) {
        ++times[chars[i] - 'a'];
    }
    //  如果该字符出现频次小于k,则不可能出现在结果子串中
    //  分别排除,然后挪动两个指针
    while (p2 - p1 + 1 >= k && times[chars[p1] - 'a'] < k) {
        ++p1;
    }
    while (p2 - p1 + 1 >= k && times[chars[p2] - 'a'] < k) {
        --p2;
    }
    if (p2 - p1 + 1 < k) return 0;
    //  得到临时子串,再递归处理
    for (int i = p1; i <= p2; ++i) {
        //  如果第i个不符合要求,切分成左右两段分别递归求得
        if (times[chars[i] - 'a'] < k) {
            return Math.max(count(chars, k, p1, i - 1), count(chars, k, i + 1, p2));
            //return Math.max(i - p1, count(chars, k, i + 1, p2));
        }
    }
    return p2 - p1 + 1;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值