【Leetcode】2516. 每种字符至少取 K 个——1948

题目

2516. 每种字符至少取 K 个

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

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

  • \(1\leq s.length\leq 10^{5}\)
  • s仅由a,b,c组成
  • \(0\leq k \leq s.length\)

思路

通过了解题目,是可以发现其实所说的每一步选择最左侧或者最右侧,最终的结果就是最后选取了左侧的一部分和右侧的一部分。

题目要的是最小选择多少个,其中至少每个字母都有k个,首先想到的就是滑动窗口。

由于是左侧一部分和右侧一部分,因此可以首先将从后往前数,数到符合条件的位置时即为右侧初始。之后左侧进行移动,在移动过程中,右侧的边界向右移动。如此滑动窗口至找到最短的。

class Solution:
    def takeCharacters(self, s: str, k: int) -> int:
        if k==0:return 0
        n = len(s)
        r = n - 1
        cnt = [0, 0, 0]
        t = 0
        while t < 3 and r >= 0:
            temp = ord(s[r]) - ord('a')
            cnt[temp] += 1
            if cnt[temp] == k:
                t += 1
            r -= 1
        if t < 3: return -1

        ans = n-r-1
        for l in range(n):
            cnt[ord(s[l]) - ord('a')] += 1
            while r + 1 < n and cnt[ord(s[r + 1]) - ord('a')] > k:
                r += 1
                cnt[ord(s[r]) - ord('a')] -= 1
            ans = min(ans, l+1+n-r-1)
        return ans

当然了,也可以懒省事,找到初始右侧的时候直接统计整个字符串,

class Solution:
    def takeCharacters(self, s: str, k: int) -> int:
        if k==0:return 0
        r = 0
        n = len(s)
        cnt = Counter(s)
        if  len(cnt.keys())!=3 or any(x<k for x in cnt.values()):
            return -1
        ans = len(s)
        for l in range(len(s)):
            while r<n and cnt[s[r]]>k:
                cnt[s[r]]-=1
                r+=1
            ans = min(ans, l+n-r)
            cnt[s[l]]+=1
        return ans

此外,也可以换一个角度出发,选择首尾一部分的同时也是选择中间的一部分。如此问题就变成了选择中间的一段,其中每个字母出现的次数不超过cnt[x]-k

class Solution:
    def takeCharacters(self, s: str, k: int) -> int:
        if k==0:return 0
        n = len(s)
        cnt = Counter(s)
        if  len(cnt.keys())!=3 or any(x<k for x in cnt.values()):
            return -1
        ans = n
        l=0
        for r in range(n):
            cnt[s[r]]-=1
            while l<=r and cnt[s[r]]<k:
                cnt[s[l]]+=1
                l+=1
            ans = min(ans,n-r+l-1)
        return ans

这里也算是利用了初始统计的cnt,实际上在后续滑动窗口的时候,cnt记录的是未选取部分,也就是我们要求的左右两端部分所包含的每个字母的个数。

三个算法的时间复杂度都是O(n)

posted @ 2024-09-27 14:06  TICSMC  阅读(13)  评论(0)    收藏  举报