【不定滑窗】2024E-最长的指定瑕疵度的元音子串
【不定滑窗】2024E-最长的指定瑕疵度的元音子串
[P3206] 【不定滑窗】2024E-最长的指定瑕疵度的元音子串
本题练习地址:https://www.algomooc.com/problem/P3206
视频直播讲解:2023/11/18 真题讲解
题目描述与示例
题目描述
头和结尾都是元音字母(aeiouAEIOU
)的字符串为元音字符串,其中混杂的非元音字母数量为其瑕疵度。比如:
"a"
,"aa"
是元音字符串,其瑕疵度都为0
"aiur"
不是元音字符串(结尾不是元音字符)"abira"
是元音字符串,其瑕疵度为2
给定一个字符串,请找出指定瑕疵度的最长元音字符子串,并输出其长度,如果找不到满足条件的元音字符子串,输出 0
。
子串:字符串中任意个连续的字符组成的子序列称为该字符串的子串。
输入描述
首行输入是一个整数,表示预期的瑕疵度flaw
,取值范围[0, 65535]
。
接下来一行是一个仅由字符a-z
和A-Z
组成的字符串,字符串长度(0, 65535]
。
输出描述
输出为一个整数,代表满足条件的元音字符子串的长度。
示例一
输入
0
asdbuiodevauufgh
输出
3
说明
满足条件的最长元音字符子串有两个,分别为uio
和auu
,长度为 3
。
示例二
输入
2
aeueo
输出
0
说明
没有满足条件的元音字符子串,输出 0
示例三
输入
1
aabeebuu
输出
5
说明
满足条件的最长元音字符子串有两个,分别为aabee
和eebuu
,长度为 5
解题思路
本题很显然是要找到一个最长的滑动窗口,窗口需要满足以下两个条件:
- 首尾的字符都是元音
- 窗口中的辅音个数为
k
个(即瑕疵度)
窗口的辅音个数,可以用一个变量win_consonant_num
来维护即可。要特别注意一点,因为元音子串要求子串首尾的字符都是元音,我们必须固定left
的位置始终指向一个元音,这使得
left
右移的条件会和常规的滑窗问题略有不同。- 在开始滑窗时,必须先找到最左边的第一个元音作为
left
的起始位置以及滑窗的开始位置。
滑窗三问
**Q1:**对于每一个右指针right
所指的元素ch
,做什么操作?
Q2:什么时候要令左指针left
右移?left
对应的元素做什么操作?while
中的循环不变量是什么?
**Q3:**什么时候进行ans
的更新?
滑窗三答
**A1:**如果ch
是一个辅音,则窗口中辅音个数win_consonant_num += 1
A2:win_consonant_num > k
,即滑窗子串所对应的瑕疵度超过了瑕疵度阈值k
,left
右移,直到窗口的瑕疵度win_consonant_num <= k
,且left
指向一个元音(因为要求元音子串开头是元音)。
**A3:**如果ch
是一个元音(因为要求元音子串末尾是元音),且此时瑕疵度恰好为k
,那么可以更新答案。
代码
Python
# 题目:【不定滑窗】2024E-最长的指定瑕疵度的元音子串
# 分值:200
# 作者:许老师-闭着眼睛学数理化
# 算法:不定滑窗
# 代码看不懂的地方,请直接在群上提问
# 输入瑕疵度
k = int(input())
# 输入原字符串
s = input()
# 构建包含元音的集合,用于快速判断某字符是不是元音
vowel_set = set("aeiouAEIOU")
n = len(s)
# 初始化滑窗的左指针left是n
left = n
# 找到第一个元音的索引
for i in range(n):
if s[i] in vowel_set:
left = i
break
# 如果经过上述循环后,left仍是n
# 说明s中不存在元音,也不存在符合要求的子串
# 直接输出0
if left == n:
print(0)
else:
ans = 0
# 初始化滑窗中的辅音个数为0
win_consonant_num = 0
# 进行滑窗,从下标left开始滑窗
for right, ch in enumerate(s[left:], left):
# A3:如果ch是一个元音,且瑕疵度恰好为k,那么可以更新答案。
if ch in vowel_set:
if win_consonant_num == k:
ans = max(ans, right-left+1)
# A1:如果ch是一个辅音,则窗口中辅音个数win_consonant_num += 1
else:
win_consonant_num += 1
# A2:即滑窗子串所对应的瑕疵度超过了瑕疵度阈值k,left右移,
# 由于元音子串的首尾必须均为元音
# 因此left需要持续右移直到以下两个条件均满足:
# 1. win_consonant_num <= k
# 2. s[left]指向一个元音
while left < n and (win_consonant_num > k or s[left] not in vowel_set):
if s[left] not in vowel_set:
win_consonant_num -= 1
left += 1
# 退出循环,ans即为答案
print(ans)
Java
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 输入瑕疵度
int k = scanner.nextInt();
scanner.nextLine(); // 读取换行符
// 输入原字符串
String s = scanner.nextLine();
// 构建包含元音的集合
Set<Character> vowelSet = new HashSet<>();
vowelSet.add('a');
vowelSet.add('e');
vowelSet.add('i');
vowelSet.add('o');
vowelSet.add('u');
vowelSet.add('A');
vowelSet.add('E');
vowelSet.add('I');
vowelSet.add('O');
vowelSet.add('U');
int n = s.length();
int left = n;
// 找到第一个元音
for (int i = 0; i < n; i++) {
if (vowelSet.contains(s.charAt(i))) {
left = i;
break;
}
}
// 如果不存在元音
if (left == n) {
System.out.println(0);
} else {
int ans = 0;
int winConsonantNum = 0;
for (int right = left; right < n; right++) {
char ch = s.charAt(right);
// 如果 ch 是元音
if (vowelSet.contains(ch)) {
if (winConsonantNum == k) {
ans = Math.max(ans, right - left + 1);
}
} else { // 如果 ch 是辅音
winConsonantNum++;
// 如果滑窗子串对应的瑕疵度超过了阈值 k
while (left < n && (winConsonantNum > k || !vowelSet.contains(s.charAt(left)))) {
if (!vowelSet.contains(s.charAt(left))) {
winConsonantNum--;
}
left++;
}
}
}
// 输出结果
System.out.println(ans);
}
}
}
C++
#include <iostream>
#include <unordered_set>
using namespace std;
int main() {
// 输入瑕疵度
int k;
cin >> k;
// 输入原字符串
string s;
cin.ignore(); // 忽略换行符
getline(cin, s);
// 构建包含元音的集合
unordered_set<char> vowelSet = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'};
int n = s.length();
int left = n;
// 找到第一个元音
for (int i = 0; i < n; i++) {
if (vowelSet.count(s[i]) == 1) {
left = i;
break;
}
}
// 如果不存在元音
if (left == n) {
cout << 0 << endl;
} else {
int ans = 0;
int winConsonantNum = 0;
int start = left;
for (int right = start; right < n; right++) {
char ch = s[right];
// 如果 ch 是元音
if (vowelSet.count(ch)) {
if (winConsonantNum == k) {
ans = max(ans, right - left + 1);
}
} else { // 如果 ch 是辅音
winConsonantNum++;
// 如果滑窗子串对应的瑕疵度超过了阈值 k
while (left < n && (winConsonantNum > k || !vowelSet.count(s[left]))) {
if (!vowelSet.count(s[left])) {
winConsonantNum--;
}
left++;
}
}
}
// 输出结果
cout << ans << endl;
}
return 0;
}
时空复杂度
时间复杂度:O(N)
。仅需一次遍历数组。
空间复杂度:O(1)
。仅需若干常数变量。