当前位置: 首页 > news >正文

力扣:3305.元音辅音字符串计数

给你一个字符串 word 和一个 非负 整数 k

返回 word 的 子字符串 中,每个元音字母('a''e''i''o''u'至少 出现一次,并且 恰好 包含 k 个辅音字母的子字符串的总数。

示例 1:

输入:word = "aeioqq", k = 1

输出:0

解释:

不存在包含所有元音字母的子字符串。

示例 2:

输入:word = "aeiou", k = 0

输出:1

解释:

唯一一个包含所有元音字母且不含辅音字母的子字符串是 word[0..4],即 "aeiou"

示例 3:

输入:word = "ieaouqqieaouqq", k = 1

输出:3

解释:

包含所有元音字母并且恰好含有一个辅音字母的子字符串有:

  • word[0..5],即 "ieaouq"
  • word[6..11],即 "qieaou"
  • word[7..12],即 "ieaouq"

提示:

  • 5 <= word.length <= 250
  • word 仅由小写英文字母组成。
  • 0 <= k <= word.length - 5

解题思路

要解决这个问题,我们需要找到所有满足以下条件的子字符串:

  1. 包含所有五个元音字母(‘a’, ‘e’, ‘i’, ‘o’, ‘u’)。
  2. 恰好包含 k 个辅音字母。

关键步骤分析:

  1. 滑动窗口遍历:使用双重循环遍历所有可能的子字符串。外层循环确定子字符串的起始位置 i,内层循环扩展子字符串的结束位置 j
  2. 辅音计数:在内层循环中,维护当前子字符串的辅音数量。若辅音数量超过 k,则提前终止当前起始位置的遍历。
  3. 元音掩码:使用位掩码(mask)来记录当前子字符串中出现的元音。每遇到一个元音,设置对应的位。
  4. 条件检查:当辅音数量恰好为 k 时,检查位掩码是否为 0x1F(二进制 11111,表示所有五个元音均存在),若满足则计数加一。
class Solution {
public:
    int countOfSubstrings(string word, int k) {
        int count = 0;
        int n = word.size();
        for (int i = 0; i < n; ++i) {
            int consonants = 0;
            int mask = 0;
            for (int j = i; j < n; ++j) {
                char c = word[j];
                if (isVowel(c)) {
                    int bit = getVowelBit(c);
                    mask |= (1 << bit);
                } else {
                    ++consonants;
                    if (consonants > k) break;
                }
                if (consonants == k && mask == 0x1F) {
                    ++count;
                }
            }
        }
        return count;
    }

private:
    bool isVowel(char c) {
        return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
    }

    int getVowelBit(char c) {
        switch (c) {
            case 'a': return 0;
            case 'e': return 1;
            case 'i': return 2;
            case 'o': return 3;
            case 'u': return 4;
            default: return -1;
        }
    }
};

上述时间复杂度:O(n^2) 

空间复杂度:O(1),仅使用常数空间存储辅音计数和元音掩码。

 

位掩码(Bitmask)的定义

位掩码是一种利用二进制位(0或1)表示状态或标志的技术。通过将多个布尔值压缩到一个整数中,每个二进制位代表一个独立的状态(例如权限、开关等),从而高效地管理和操作多个状态。

位掩码的核心用途

  1. 状态压缩
    将多个二元状态(如“是/否”)合并到一个整数中,节省内存。
  2. 快速查询与修改
    通过位运算(AND、OR、NOT等)快速检查或修改特定状态。
  3. 组合与筛选
    灵活组合不同状态,例如权限系统的“读+写+执行”组合。

 官方题解

方法一:暴力枚举
枚举字符串 word 的所有子字符串,统计每个元音字母都出现且辅音字母出现次数等于 k 的子字符串数目。
class Solution {
public:
    long long countOfSubstrings(string word, int k) {
        set<char> vowels = {'a', 'e', 'i', 'o', 'u'};
        int n = word.size();
        long long res = 0;
        for (int i = 0; i < n; i++) {
            set<char> occur;
            int consonants = 0;
            for (int j = i; j < n; j++) {
                if (vowels.count(word[j])) {
                    occur.insert(word[j]);
                } else {
                    consonants++;
                }
                if (occur.size() == vowels.size() && consonants == k) {
                    res++;
                }
            }
        }
        return res;
    }
};


复杂度分析

时间复杂度:O(n^2),其中 n 是 word 的长度。

空间复杂度:O(1)。

方法二:滑动窗口
令 count(k) 表示每个元音字母至少出现一次,并且至少包含 k 个辅音字母的子字符串的总数,那么本问题的答案等于 count(k)−count(k+1)。对于 count(k),我们可以使用滑动窗口来求解。

对于区间 [i,j) 内的子字符串,我们依次枚举子字符串的左端点 i,对于对应的右端点 j,我们不断地右移右端点 j,直到区间 [i,j) 对应的子字符满足每个元音字母至少出现一次,并且至少包含 k 个辅音字母,或者 j=n。右移操作完成后,如果区间 [i,j) 内的子字符串满足每个元音字母至少出现一次,并且至少包含 k 个辅音字母,那么左端点为 i 的子字符串满足条件的数目为 n−j+1。count(k) 即为所有数目之和。

class Solution {
public:
    long long countOfSubstrings(string word, int k) {
        set<char> vowels = {'a', 'e', 'i', 'o', 'u'};
        auto count = [&](int m) -> long long {
            int n = word.size(), consonants = 0;
            long long res = 0;
            map<char, int> occur;
            for (int i = 0, j = 0; i < n; i++) {
                while (j < n && (consonants < m || occur.size() < vowels.size())) {
                    if (vowels.count(word[j])) {
                        occur[word[j]]++;
                    } else {
                        consonants++;
                    }
                    j++;
                }
                if (consonants >= m && occur.size() == vowels.size()) {
                    res += n - j + 1;
                }
                if (vowels.count(word[i])) {
                    occur[word[i]]--;
                    if (occur[word[i]] == 0) {
                        occur.erase(word[i]);
                    }
                } else {
                    consonants--;
                }
            }
            return res;
        };
        return count(k) - count(k + 1);
    }
};


复杂度分析

时间复杂度:O(n),其中 n 是 word 的长度。

空间复杂度:O(1)。

作者:力扣官方题解
链接:https://leetcode.cn/problems/count-of-substrings-containing-every-vowel-and-k-consonants-i/solutions/3077748/yuan-yin-fu-yin-zi-fu-chuan-ji-shu-i-by-r8rjy/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

ps:本问题的答案等于 count(k)−count(k+1)

相关文章:

  • Guangzhaotest
  • 动态规划中的自底向上与自顶向下
  • 【A2DP】深入解析A2DP协议中的音频流处理
  • 数字孪生技术在工业制造中的应用探索
  • MongoDB副本集部署完整教程
  • 逐梦DBA:Linux环境下 MySQL 的卸载
  • 嵌入式八股C语言---文件,可执行文件的加载与运行篇
  • MySQL——基础知识
  • 【机器学习-基础知识】统计和贝叶斯推断
  • Flutter_学习记录_device_info_plus 插件获取设备信息
  • 12 | 给应用添加优雅关停功能
  • Webpack 优化深度解析:从构建性能到输出优化的全面指南
  • Vue项目上传到GitHub,vscode拉取vue项目更新后推送到GitHub上
  • vllm.LLM 的参数
  • Linux——进程初步
  • FI模块功能范围的基本概念、用途、配置介绍
  • java登神之阶之顺序表
  • SpringAI+Ollama+DeepSeek本地大模型调用
  • yolov8在昇腾芯片上的测试
  • ESP32-S3-WROOM-1-N16R8 微控制器,搭配 SI4732-A10-GSR 的收音机(源码+PCB+3D模型)
  • 印方称所有敌对行动均得到反击和回应,不会升级冲突
  • 4月金融数据前瞻:受去年低基数因素影响,社融增量有望同比大幅多增
  • 马上评丨行人转身相撞案:走路该保持“安全距离”吗
  • 秦洪看盘|重估叙事主题卷土重来,给A股注入新活力
  • 习近平同俄罗斯总统普京举行会谈
  • 云南临沧一行贿案金额认定比受贿案多41万,重审时检方变更金额起诉