【精讲】leetcode 2516.每种字符至少取 K 个

hello大家好!总算是熬过了期中,太痛苦了QAQ明天还要考一千米,心碎了一地.....

本来想着接着更数据结构的,但是时间上有点排不过来了,就给大家分享一题,我觉得很有意思的力扣题目,整整花了我三个小时才搞明白,个人感觉比那些困难的题目还难QAQ

题目:

给你一个由字符 'a''b''c' 组成的字符串 s 和一个非负整数 k 。每分钟,你可以选择取走 s 最左侧 还是 最右侧 的那个字符。

你必须取走每种字符 至少 k 个,返回需要的 最少 分钟数;如果无法取到,则返回 -1 。

示例:

示例 1:

输入:s = "aabaaaacaabc", k = 2
输出:8
解释:
从 s 的左侧取三个字符,现在共取到两个字符 'a' 、一个字符 'b' 。
从 s 的右侧取五个字符,现在共取到四个字符 'a' 、两个字符 'b' 和两个字符 'c' 。
共需要 3 + 5 = 8 分钟。
可以证明需要的最少分钟数是 8 。

示例 2:

输入:s = "a", k = 1
输出:-1
解释:无法取到一个字符 'b' 或者 'c',所以返回 -1 。

提示:

  • 1 <= s.length <= 105
  • s 仅由字母 'a''b''c' 组成
  • 0 <= k <= s.length

思路:

(其实我一直觉得力扣给的这个提示,没什么用啊)这个题目解法我大概看了一下,有正面暴力破解,二分法,反向思维。我题解和ai看了半天,也看了人家的教学视频才完全弄懂着玩意儿。我最终选择了反向思维这个方法,因为我觉得,记住这种解决问题的模式,对我的思维提升很有帮助。底下也会详细介绍一下这个办法。

首先是暴力破解法。但其实说是这么说,也有简化的思维在里面。就是说,因为这题是可以在左右两边都进行提取的操作,但是如果我们两边同时考虑,就会变得很复杂,所以我们就先考虑单边取到极限的特殊情况,如下图(我这里就用题目给的例子来画图了)

#即设置两个指针,让左边保持初始0位置不动,右侧向左遍历,并且记录abc三个字母出现的次数,当所有字母出现次数都大于等于2时,停止遍历。开始考虑左边的情况。左边指针也是一样的操作,向右侧遍历,每次遍历都给相应的字符的数量加1,但是同时也要检查,右边指针是否满足条件可以向右边移动,以此来减少操作的次数。在图中例子中,只有左指针到b时,右指针才能动,因为此时b的数量为3,就可以向右侧移动,直到到c时停下,因为c的数量是2,再动就不符合要求了。

但这个方法其实非常的繁琐,代码页也比较复杂,所以这里还是推荐大家使用反向思维——正难则反,要求左右两侧最小的数组和,就是求整个数组剪掉最大的不要的部分数组

先上个代码给大家瞅瞅

from collections import Counter

class Solution:
    def takeCharacters(self, s: str, k: int) -> int:
        # 统计每个字符的出现次数
        cnt = Counter(s)
        
        # 如果任何字符的数量小于 k,直接返回 -1
        if any(cnt[c] < k for c in "abc"):
            return -1
        
        # 记录窗口内有效字符的最大长度
        mx = left = 0
        
        # 遍历字符串,用 right 来表示窗口的右边界
        for right, c in enumerate(s):
            cnt[c] -= 1  # 当前字符移入窗口,减少计数
            
            # 如果当前字符的数量不足 k,收缩窗口
            while cnt[c] < k:
                cnt[s[left]] += 1  # 移出左边界的字符,恢复计数
                left += 1  # 移动左指针
            
            # 更新最大有效窗口长度,为什么这么计数,其实写个实例算一下就知道了
            mx = max(mx, right - left + 1)
        
        # 返回需要去除的字符数,等于总字符数减去最大有效窗口长度
        return len(s) - mx


代码解析:

1.统计字符频率: 使用 Counter 来统计字符串中每个字符出现的次数。

from collections import Counter
s = "aabaaaacaabc"
cnt = Counter(s)
print(cnt)  # {'a': 8, 'b': 2, 'c': 2}

2.检查是否满足条件: 如果某个字符的频率小于 k,则直接返回 -1,表示无法完成任务。

这里,'a' 出现了 8 次,'b''c' 都出现了 2 次,都是大于或等于 k = 2,所以继续执行。

3.滑动窗口: 我们使用滑动窗口的技术来查找最少的操作次数。具体过程如下:

  • mx 用来记录窗口内有效字符的最大长度,left 是窗口的左边界,right 是窗口的右边界。
  • 我们从 right = 0 开始遍历字符串,并根据字符的频率来调整窗口。

还是上一张图帮助大家理解

#我把大概的过程分析都写出来了,看不懂代码的朋友可以看看图中的实例,一定要注意for循环和while循环的区别,我自己当时因为这个问题卡了一个小时QAQ

结语:最近真是忙成一团,前面的数据结构与算法系列可能会再更新数据结构的补充,算法可能就要到放假我开始疯狂刷题的时候再说了QAQ

没办法,学校要考试,我也只能顺从了,虽然我感觉学他教的哪些根本没啥用...

OK,本期文章就到这里啦,小编要滚去做神经网络的项目了,祝大家学习生活顺利!!!

古德拜~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值