3306. Count of Substrings Containing Every Vowel and K Consonants II

You are given a string word and a non-negative integer k.

Return the total number of substrings of word that contain every vowel ('a''e''i''o', and 'u'at least once and exactly k consonants.

Example 1:

Input: word = “aeioqq”, k = 1

Output: 0

Explanation:

There is no substring with every vowel.

Example 2:

Input: word = “aeiou”, k = 0

Output: 1

Explanation:

The only substring with every vowel and zero consonants is word[0..4], which is "aeiou".

Example 3:

Input: word = “ieaouqqieaouqq”, k = 1

Output: 3

Explanation:

The substrings with every vowel and one consonant are:

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

Constraints:

  • 5 <= word.length <= 2 * 105
  • word consists only of lowercase English letters.
  • 0 <= k <= word.length - 5

Approach 01:

#include <unordered_map>
#include <string>
#include <algorithm>

class Solution {
public:
    long long countOfSubstrings(std::string word, int maxNonVowel) {
        return countSubstringsWithAtMost(word, maxNonVowel) - 
               countSubstringsWithAtMost(word, maxNonVowel - 1);
    }

private:
    long countSubstringsWithAtMost(const std::string& word, int maxNonVowel) {
        if (maxNonVowel == -1)
            return 0;

        long substringCount = 0;
        int vowelCount = 0;
        int uniqueVowelCount = 0;
        std::unordered_map<char, int> lastVowelPosition;

        for (int left = 0, right = 0; right < word.length(); ++right) {
            if (isVowel(word[right])) {
                ++vowelCount;
                if (const auto it = lastVowelPosition.find(word[right]); 
                    it == lastVowelPosition.end() || it->second < left)
                    ++uniqueVowelCount;
                lastVowelPosition[word[right]] = right;
            }

            while (right - left + 1 - vowelCount > maxNonVowel) {
                if (isVowel(word[left])) {
                    --vowelCount;
                    if (lastVowelPosition[word[left]] == left)
                        --uniqueVowelCount;
                }
                ++left;
            }

            if (uniqueVowelCount == 5) {
                substringCount += std::min({
                    lastVowelPosition['a'], lastVowelPosition['e'], 
                    lastVowelPosition['i'], lastVowelPosition['o'], 
                    lastVowelPosition['u']
                }) - left + 1;
            }
        }

        return substringCount;
    }

    bool isVowel(char character) {
        static constexpr std::string_view vowels = "aeiou";
        return vowels.find(character) != std::string_view::npos;
    }
};

Time Complexity:

  • Sliding Window Traversal:

    Both calls to countSubstringsWithAtMost use a two-pointer approach (left and right), ensuring each character is processed at most twice, resulting in \( O(n) \) complexity.

  • Map Operations:

    Updating and accessing lastVowelPosition takes \( O(1) \) per operation, leading to \( O(n) \) total operations.

  • Total Time Complexity:

    Since countSubstringsWithAtMost is called twice, the overall complexity remains \( O(n) \).

Space Complexity:

  • Map Storage:

    The unordered map lastVowelPosition stores at most 5 vowel positions, contributing to \( O(1) \) space.

  • Other Variables:

    All other variables use \( O(1) \) additional space.

  • Total Space Complexity:

    The overall space complexity is \( O(1) \).

from collections import defaultdict

class Solution:
    def countOfSubstrings(self, word: str, maxNonVowel: int) -> int:
        return (self._countSubstringsWithAtMost(word, maxNonVowel) - 
                self._countSubstringsWithAtMost(word, maxNonVowel - 1))

    def _countSubstringsWithAtMost(self, word: str, maxNonVowel: int) -> int:
        if maxNonVowel == -1:
            return 0

        substringCount = 0
        vowelCount = 0
        uniqueVowelCount = 0
        lastVowelPosition = {}

        left = 0
        for right in range(len(word)):
            if self._isVowel(word[right]):
                vowelCount += 1
                if word[right] not in lastVowelPosition or lastVowelPosition[word[right]] < left:
                    uniqueVowelCount += 1
                lastVowelPosition[word[right]] = right

            while right - left + 1 - vowelCount > maxNonVowel:
                if self._isVowel(word[left]):
                    vowelCount -= 1
                    if lastVowelPosition[word[left]] == left:
                        uniqueVowelCount -= 1
                left += 1

            if uniqueVowelCount == 5:
                substringCount += min(
                    lastVowelPosition.get('a', float('inf')),
                    lastVowelPosition.get('e', float('inf')),
                    lastVowelPosition.get('i', float('inf')),
                    lastVowelPosition.get('o', float('inf')),
                    lastVowelPosition.get('u', float('inf'))
                ) - left + 1

        return substringCount

    def _isVowel(self, character: str) -> bool:
        return character in "aeiou"

Time Complexity:

  • Sliding Window Traversal:

    Both _countSubstringsWithAtMost calls iterate through the string using two pointers (left and right), ensuring each character is processed at most twice. This results in \( O(n) \) complexity.

  • Dictionary Operations:

    Each vowel update in lastVowelPosition is \( O(1) \), and fetching the minimum last position of all vowels is \( O(1) \), leading to \( O(n) \) overall operations.

  • Total Time Complexity:

    The function calls _countSubstringsWithAtMost twice, making the final complexity \( O(n) \).

Space Complexity:

  • Dictionary Storage:

    The dictionary lastVowelPosition stores at most 5 vowel positions, contributing to \( O(1) \) space.

  • Other Variables:

    All other variables use \( O(1) \) additional space.

  • Total Space Complexity:

    The overall space complexity is \( O(1) \).

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top