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

给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。

示例 1:

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

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

题意回顾
给你字符串 s,所谓合法子串 T:T中的每个字符的出现次数都不小于 k。求出 T 子串的最长长度。

思路展开
先整体考虑,如果某个字符在整个字符串中的出现次数 < k,那它一定不会出现在合法子串中。
s: aaabbaa,k: 3,b 只出现 2 次,它肯定不会出现在合法子串中,要到它的两侧找。
考察aaa和aa,变成一个规模较小的子问题,递归去求aaa和aa中合法子串的最长长度。
当递归到子问题的规模足够小,即,子串的长度小于 k,即便子串的字符都相同,字符的出现次数也小于 k,所以没有合法子串,返回 0
充分剪枝
当子串的长度小于 k 就返回 0,结束递归,而不是用「不再构成区间(start > end)」作为递归的出口,即提前回溯,不做不必要的搜索。
如果当前递归的子串的两端,即 start 和 end 上的字符,在当前子串中出现的次数小于 k,则合法子串肯定不包含,移动指针,直到指向不小于k的字符。有些状态直接不考察,也是在剪枝。

#define MAX(a, b) (a) > (b) ? (a) : (b)
int LongestSubString(char *s, int start, int end, int k)
{
    if ((start >= end) || ((end - start) < k)) { // 区间的长度小于k,不存在满足条件的T串
        return 0;
    }

    int array[26];
    memset(array, 0, sizeof(array));
    for (int i = start; i < end; i++) { // 统计当前区间的字符的出现次数
        array[s[i] - 'a']++;
    }
    // 在区间长度>=k的前提下,如果start位置上的字符出现的次数小于k
    while ((end - start >= k) && (array[s[start] - 'a'] < k)) {
        start++; // 则T子串肯定不包含这个start字符,start指针右移
    }
    // 在区间长度>=k的前提下,如果end位置上的字符出现的次数小于k
    while ((end - start >= k) && (array[s[end - 1] - 'a'] < k)) {
        end--; // 则T子串肯定不包含这个end字符,end指针左移
    }

    int sepratePos = -1;
    for (int i = start; i < end; i++) {
        if (array[s[i] - 'a'] < k) { // 如果区间长度因此而 < k,直接返回0
            sepratePos = i;
            break;
        }
    }
    if (sepratePos == -1) {
        return end - start;
    }

    int result = LongestSubString(s, start, sepratePos, k);
    if (result > (end - sepratePos - 1)) {
        return result;
    }
    result = MAX(result, LongestSubString(s, sepratePos + 1, end, k));

    return result;
}

int longestSubstring(char * s, int k){
    int len = strlen(s);
    if (k == 1) {
        return len;
    }
    if (len < k) {
        return 0;
    }

    return LongestSubString(s, 0, strlen(s), k);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,有三种方法可以解决LeetCode上的最长回文子串问题。 方法一是使用扩展心法优化,即从左向右遍历字符串,找到连续相同字符组成的子串作为扩展心,然后从该心向左右扩展,找到最长的回文子串。这个方法的时间复杂度为O(n²)。\[1\] 方法二是直接循环字符串,判断子串是否是回文子串,然后得到最长回文子串。这个方法的时间复杂度为O(n³),效率较低。\[2\] 方法三是双层for循环遍历所有子串可能,然后再对比是否反向和正向是一样的。这个方法的时间复杂度也为O(n³),效率较低。\[3\] 综上所述,方法一是解决LeetCode最长回文子串问题的最优解法。 #### 引用[.reference_title] - *1* [LeetCode_5_最长回文子串](https://blog.csdn.net/qq_38975553/article/details/109222153)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Leetcode-最长回文子串](https://blog.csdn.net/duffon_ze/article/details/86691293)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [LeetCode 第5题:最长回文子串(Python3解法)](https://blog.csdn.net/weixin_43490422/article/details/126479629)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值