LeetCode 1456:定长子串中元音的最大数目

LeetCode 1456:定长子串中元音的最大数目

    • [1. 题目介绍](#1. 题目介绍)
    • [2. 解题思路](#2. 解题思路)
    • [3. 示例代码](#3. 示例代码)
      • [3.1 解法一:暴力枚举](#3.1 解法一:暴力枚举)
      • [3.2 解法二:滑动窗口](#3.2 解法二:滑动窗口)
    • [4. 拓展:滑动窗口可视化页面](#4. 拓展:滑动窗口可视化页面)
    • [5. 总结](#5. 总结)

🎬 博主名称: 超级苦力怕

🔥 个人专栏: 《LeetCode 题解》

🚀 每一次思考都是突破的前奏,每一次复盘都是精进的开始!


本篇文章给大家带来的是 LeetCode 第 1456 题------定长子串中元音的最大数目 。这是一道 滑动窗口的经典入门题 ,虽然题目以元音计数为背景,但核心思想「固定长度窗口的滑动」是所有滑动窗口问题的通用解法,掌握后可以轻松解决类似问题。

本文将采用 Java 语言进行讲解,从暴力解法出发,逐步优化到滑动窗口,帮助大家深入理解滑动窗口的核心原理。话不多说,让我们开始吧!

1. 题目介绍

1456. 定长子串中元音的最大数目

直达链接:LeetCode 1456

给你字符串 s 和整数 k

请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。

英文中的元音字母为:a, e, i, o, u

2. 解题思路

这道题要求在字符串 s 中找到长度为 k 的子串中,元音字母数量最多的那一个。关键在于理解题意:我们需要遍历所有长度为 k 的子串,统计每个子串中的元音个数,然后返回最大值。

解法一:暴力枚举

算法思想:

  • 枚举所有长度为 k 的子串
  • 对每个子串遍历统计元音字母个数
  • 取所有子串中元音数量的最大值

复杂度分析:

  • 时间复杂度:O(n * k),需要遍历每个子串的每个字符
  • 空间复杂度:O(1)

解法二:滑动窗口(推荐)

通过假设一个长度为k,可以移动的窗口,这样每一次移动窗口的时候,我们只需要考虑最右边和最左边的字符。

实现思路 :计算出第一个窗口abc 的元音个数,那么从子串abc移动到bci ,我们只需要考虑离开窗口的字母a是不是元音,以及进入窗口的字母i是不是元音即可,其他字母都在窗口内,因此不需要判断。

核心代码:文字描述(以示例1为例:s = "abciiidef", k = 3)

复制代码
以示例1为例:s = "abciiidef", k = 3  
这里的 vowel 代指当前窗口所含元音字母

1. 初始化窗口 [0,2] -> "abc"
   - 此时已记录 'abc',vowel = 1

2. 窗口向右滑动到 [1,3] -> "bci"
   - 进入窗口: 'i'(是元音) -> vowel = 2
   - 离开窗口: 'a'(是元音) -> vowel = 1
   - 更新答案: maxCount = max(1, 2) = 2

3. 窗口向右滑动到 [2,4] -> "cii"
   - 进入窗口: 'i'(是元音) -> vowel = 2
   - 离开窗口: 'b'(否) -> vowel = 2

4. 窗口向右滑动到 [3,5] -> "iii"
   - 进入窗口: 'i'(是元音) -> vowel = 3
   - 离开窗口: 'c'(否) -> vowel = 3
   - 更新答案: maxCount = max(2, 3) = 3

最终答案: maxCount = 3

复杂度分析

  • 时间复杂度:O(n),每个字符最多被访问两次
  • 空间复杂度:O(1),只使用常数额外空间
  • 相比暴力枚举(需要重复计算窗口内字符),滑动窗口利用了窗口移动的规律,避免了重复计算

3. 示例代码

3.1 解法一:暴力枚举

Java 实现:

java 复制代码
class Solution {
    public int maxVowels(String s, int k) {
        // 使用 HashSet 存储元音字母,方便 O(1) 时间复杂度查找
        Set<Character> vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u'));
        
        // 用于记录所有子串中元音数量的最大值
        int maxCount = 0;
        
        // 枚举所有长度为 k 的子串的起始位置 i
        // 起始位置范围:0 到 s.length() - k
        for (int i = 0; i <= s.length() - k; i++) {
            // 统计当前子串 s[i, i+k) 中的元音数量
            int count = 0;
            
            // 遍历当前窗口内的所有字符
            for (int j = i; j < i + k; j++) {
                // 如果当前字符是元音,则计数加一
                if (vowels.contains(s.charAt(j))) {
                    count++;
                }
            }
            
            // 更新最大元音数量
            maxCount = Math.max(maxCount, count);
        }
        
        // 返回最大元音数量
        return maxCount;
    }
}

3.2 解法二:滑动窗口

核心思想:维护一个长度为 k 的滑动窗口,通过不断向右移动窗口,利用上一次窗口的结果快速计算新窗口的元音数量,避免重复计算。

java 复制代码
class Solution {
    public int maxVowels(String S, int k) {
        // 将字符串转换为字符数组,提高访问效率
        char[] s = S.toCharArray();
        
        // ans:记录遍历过程中遇到的最大元音数量
        int ans = 0;
        // vowel:记录当前窗口内的元音数量
        int vowel = 0;
        
        // 遍历字符串,枚举每个可能成为窗口右端点的位置 i
        // i 的取值范围:0 到 s.length - 1
        for (int i = 0; i < s.length; i++) {
            
            // ===== 步骤 1:将当前右端点字符加入窗口 =====
            // 判断当前字符是否是元音,如果是则元音计数加一
            if (s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u') {
                vowel++;
            }

            // 计算当前窗口的左端点位置
            // 当 i = k-1 时,left = 0,此时窗口大小正好为 k
            int left = i - k + 1;
            
            // 如果左端点位置小于 0,说明窗口大小还不足 k
            // 尚未形成第一个完整的窗口,继续下一轮循环
            if (left < 0) {
                continue;
            }

            // ===== 步骤 2:更新答案 =====
            // 当窗口大小达到 k 时,比较并更新最大元音数量
            ans = Math.max(ans, vowel);

            // ===== 步骤 3:将当前左端点字符移出窗口 =====
            // 为下一轮循环做准备,下一轮窗口会向右移动一位
            // 记录即将离开窗口的字符
            char out = s[left];
            // 如果离开的字符是元音,则元音计数减一
            if (out == 'a' || out == 'e' || out == 'i' || out == 'o' || out == 'u') {
                vowel--;
            }
        }
        
        // 返回所有窗口中元音数量的最大值
        return ans;
    }
}

优化思路:窗口的最大长度为 k,当窗口内的元音字母有 k 个时,就已经确定答案(不可能有更大的了),可以直接提前返回,避免不必要的遍历。

java 复制代码
class Solution {
    public int maxVowels(String S, int k) {
        char[] s = S.toCharArray();
        
        int ans = 0;
        int vowel = 0;
        for (int i = 0; i < s.length; i++) {
            
            // ===== 步骤 1:将当前右端点字符加入窗口 =====
            if (s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u') {
                vowel++;
            }

            int left = i - k + 1;

            if (left < 0) {
                continue;
            }

            // ===== 步骤 2:更新答案 =====
            // 当窗口大小达到 k 时,比较并更新最大元音数量
            ans = Math.max(ans, vowel);
            
            // ===== 优化:提前返回 =====
            // 如果当前答案已经等于窗口长度 k,说明已达到理论最大值
            // 不可能找到比 k 更大的元音数量了,可以直接返回
            if (ans == k) {
                return k;
            }

            // ===== 步骤 3:将当前左端点字符移出窗口 =====
            char out = s[left];
            if (out == 'a' || out == 'e' || out == 'i' || out == 'o' || out == 'u') {
                vowel--;
            }
        }
        
        return ans;
    }
}

4. 拓展:滑动窗口可视化页面

视频播放页面下载地址:github仓库,如果打不开请选择:gitee仓库,在后续会不断更新使用到的HTML页面。

5. 总结

解法 时间复杂度 空间复杂度 推荐指数
滑动窗口 O(n) O(1) ⭐⭐⭐⭐⭐
暴力枚举 O(n * k) O(1) ⭐⭐⭐

核心要点:

  1. 滑动窗口是本题的最优解法,充分利用了窗口移动的特性
  2. 判断元音可以使用 set 存储,也可以直接字符比较(代码中两种方式都有体现)
  3. 注意边界条件的处理
相关推荐
小O的算法实验室2 小时前
2026年IEEE IOTJ,DNA序列启发相似性驱动粒子群算法+无人机与基站部署,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
谭欣辰2 小时前
Floyd算法:动态规划解最短路径
c++·算法·图论
计算机安禾2 小时前
【Linux从入门到精通】第12篇:进程的前后台切换与信号控制
linux·运维·算法
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 84. 柱状图中最大的矩形 | C++ 两次单调栈基础扫法
c++·算法·leetcode
C雨后彩虹2 小时前
文件目录大小
java·数据结构·算法·华为·面试
0南城逆流02 小时前
【技术点】嵌入式技术考点三:数据结构
java·数据结构·算法
罗湖老棍子2 小时前
Beads(信息学奥赛一本通- P1461) [POI 2010] KOR-Beads(洛谷-P3498)
算法·字符串·哈希
paeamecium2 小时前
【PAT甲级真题】- Favorite Color Stripe (30)
数据结构·c++·算法·pat
DeepModel2 小时前
机器学习数据预处理:特征构造
人工智能·学习·算法·机器学习