leetcode力扣刷题系列——【每种字符至少取 K 个】

题目

给你一个由字符 ‘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

答案:

为什么今天只有答案呢,以为这个题我没有靠自己做出来,而是看了答案后做出来得,所以这道题我不配告诉大家怎么写,那么让我们看看官方的答案和解题思路吧。
先看解题思路
思路
按照题意所述,从最左和最右侧取走后,原字符串还剩下一个连续的区间,那么可以转化为求一个最长的子区间,使得区间两边的所有字符加起来满足题目要求。当满足题意要求时,显然区间长度越长,取得的字符就越少。所以当右端点 r 固定时,最优的情况是找到一个最小的左端点 l 使得取走的字符最少,并且随着左端点 r 右移动,满足要求的 l 也会往右移动。针对这种情况,可以采用双指针的做法,先扫一遍使得每个字符都进行计数然后存到 cnt 数组中,如果不满足题意要求直接返回 −1 即可。左右指针一开始都从 0 开始,优先移动右指针 r,每移动一次 r 表示把这个字符添加到最后还剩下的集合中,所以应该在计数中减去。

  • 如果此时计数数组 cnt 中每一个元素都大于等于 k,则表示满足题目要求直接更新答案。
  • 如果 cnt 中存在一个元素小于 k,则移动左指针 l,表示要拿掉这个字符。所以应该计数添加到 cnt
    中,持续移动左指针直到满足题目要求。

答案

class Solution:
    def takeCharacters(self, s: str, k: int) -> int:
        cnt = [0] * 3
        ans = len(s)

        for c in s:
            cnt[ord(c) - ord('a')] += 1
        if cnt[0] >= k and cnt[1] >= k and cnt[2] >= k:
            ans = min(ans, len(s))
        else:
            return -1

        l = 0
        for r, ch in enumerate(s):
            cnt[ord(ch) - ord('a')] -= 1
            while l < r and (cnt[0] < k or cnt[1] < k or cnt[2] < k):
                cnt[ord(s[l]) - ord('a')] += 1
                l += 1
            if cnt[0] >= k and cnt[1] >= k and cnt[2] >= k:
                ans = min(ans, len(s) - (r - l + 1))

        return ans

作者:力扣官方题解
链接:这个是链接地址
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

火眼猊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值