题目描述
中等
提示
给你字符串 s 和整数 k 。
请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。
英文中的 元音字母 为(a, e, i, o, u)。
示例 1:
输入: s = "abciiidef", k = 3
输出: 3
解释: 子字符串 "iii" 包含 3 个元音字母。
示例 2:
输入: s = "aeiou", k = 2
输出: 2
解释: 任意长度为 2 的子字符串都包含 2 个元音字母。
示例 3:
输入: s = "leetcode", k = 3
输出: 2
解释: "lee"、"eet" 和 "ode" 都包含 2 个元音字母。
示例 4:
输入: s = "rhythms", k = 4
输出: 0
解释: 字符串 s 中不含任何元音字母。
示例 5:
输入: s = "tryhard", k = 4
输出: 1
提示:
1 <= s.length <= 10^5s由小写英文字母组成1 <= k <= s.length
思路
我们要计算所有长度恰好为 k 的子串中,最多可以包含多少个元音字母。
假如我们已经计算出了子串 abc 的元音个数,那么从子串 abc 到子串 bci,只需要考虑移除(离开窗口)的字母 a 是不是元音,以及添加(进入窗口)的字母 i 是不是元音即可,因为中间的字母 b 和 c 都在这两个子串中。
举例
示例 1,s=abciiidef, k=3。
从左到右遍历 s。
首先统计前 k−1=2 个字母的元音个数,这有 1 个。
s[2]=c 进入窗口,此时找到了第一个长为 k 的子串 abc,现在元音个数有 1 个,更新答案最大值。然后 s[0]=a 离开窗口,现在元音个数有 0 个。
s[3]=i 进入窗口,此时找到了第二个长为 k 的子串 bci,现在元音个数有 1 个,更新答案最大值。然后 s[1]=b 离开窗口,现在元音个数有 1 个。
s[4]=i 进入窗口,此时找到了第三个长为 k 的子串 cii,现在元音个数有 2 个,更新答案最大值。然后 s[2]=c 离开窗口,现在元音个数有 2 个。
s[5]=i 进入窗口,此时找到了第四个长为 k 的子串 iii,现在元音个数有 3 个,更新答案最大值。然后 s[3]=i 离开窗口,现在元音个数有 2 个。
s[6]=d 进入窗口,此时找到了第五个长为 k 的子串 iid,现在元音个数有 2 个,更新答案最大值。然后 s[4]=i 离开窗口,现在元音个数有 1 个。
s[7]=e 进入窗口,此时找到了第六个长为 k 的子串 ide,现在元音个数有 2 个,更新答案最大值。然后 s[5]=i 离开窗口,现在元音个数有 1 个。
s[8]=f 进入窗口,此时找到了第七个长为 k 的子串 def,现在元音个数有 1 个,更新答案最大值。遍历结束。
定长滑窗套路
总结成三步:入-更新-出。
入:下标为 i 的元素进入窗口,更新相关统计量。如果 i<k−1 则重复第一步。
更新:更新答案。一般是更新最大值/最小值。
出:下标为 i−k+1 的元素离开窗口,更新相关统计量。
以上三步适用于所有定长滑窗题目。
const AllVowels = "aeiou"
func maxVowels(s string, k int) (ans int) {
vowel := 0
for i := 0; i < len(s); i++ {
if IsVowels(s[i]) {
vowel++
}
if i-k+1 < 0 {
continue
}
ans = max(ans, vowel)
if out := s[i-k+1]; IsVowels(out) {
vowel--
}
}
return ans
}
func IsVowels(ch byte) bool {
for i := 0; i < len(AllVowels); i++ {
if ch == AllVowels[i] {
return true
}
}
return false
}