【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)

浙公网安备 33010602011771号