Facebook Pixel

3120. Count the Number of Special Characters I

EasyHash TableString
Leetcode Link

Problem Description

You are given a string word that contains letters. A letter is considered special if both its lowercase and uppercase versions appear somewhere in the string word.

For example, if the string contains both 'a' and 'A', then the letter 'a'/'A' is special. However, if the string only contains 'b' but not 'B', then 'b' is not special.

Your task is to count how many distinct letters in word are special and return this count.

The solution works by first creating a set s containing all unique characters from the input string. Then it iterates through all 26 letters of the alphabet (using ascii_lowercase and ascii_uppercase which represent 'a-z' and 'A-Z' respectively). For each letter, it checks if both the lowercase and uppercase versions exist in the set. The sum() function counts how many letter pairs satisfy this condition, giving us the total number of special letters.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we need to check for the presence of matching letter pairs (lowercase and uppercase versions of the same letter). Since we only care about whether a character exists in the string or not, we can use a set to store all unique characters from the word - this gives us O(1) lookup time for checking existence.

Once we have all characters in a set, we need to systematically check each of the 26 possible letters to see if both versions are present. We can think of this as checking 26 pairs: ('a', 'A'), ('b', 'B'), ... ('z', 'Z').

Python's zip(ascii_lowercase, ascii_uppercase) elegantly creates these pairs for us. For each pair (a, b), we check if both a in s and b in s. If both conditions are true, that letter is special.

Using sum() with a generator expression is a concise way to count how many of these 26 pairs satisfy our condition - it treats True as 1 and False as 0, effectively counting the number of special letters.

This approach is efficient because we only make one pass through the input string to build the set, and then we perform a constant number of operations (26 checks) regardless of the input size.

Solution Approach

The solution uses a hash table (set) to efficiently track which characters appear in the string.

Step 1: Create a set of all characters

s = set(word)

We convert the input string word into a set s. This automatically removes duplicates and gives us all unique characters that appear in the string. The set provides O(1) lookup time for checking character existence.

Step 2: Check all 26 letter pairs

sum(a in s and b in s for a, b in zip(ascii_lowercase, ascii_uppercase))

This step breaks down into several parts:

  • ascii_lowercase contains 'abcdefghijklmnopqrstuvwxyz'
  • ascii_uppercase contains 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  • zip(ascii_lowercase, ascii_uppercase) pairs them up: ('a', 'A'), ('b', 'B'), ..., ('z', 'Z')

Step 3: Count special letters For each pair (a, b):

  • Check if lowercase letter a exists in set s
  • Check if uppercase letter b exists in set s
  • If both exist (a in s and b in s evaluates to True), this letter is special

The sum() function counts how many pairs satisfy both conditions. Since Python treats True as 1 and False as 0, summing the boolean results gives us the total count of special letters.

Time Complexity: O(n) where n is the length of the input string (for creating the set) + O(26) for checking all letters = O(n)

Space Complexity: O(n) for storing the set of characters

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with the string word = "aaAbcBC".

Step 1: Create a set of all characters

s = set("aaAbcBC")
# s = {'a', 'A', 'b', 'c', 'B', 'C'}

The set automatically removes duplicate 'a' characters and stores only unique characters.

Step 2: Check all 26 letter pairs

We'll iterate through each letter pair and check if both versions exist in our set:

  • For pair ('a', 'A'):

    • 'a' in s? ✓ (Yes)
    • 'A' in s? ✓ (Yes)
    • Both exist → 'a' is special (count = 1)
  • For pair ('b', 'B'):

    • 'b' in s? ✓ (Yes)
    • 'B' in s? ✓ (Yes)
    • Both exist → 'b' is special (count = 2)
  • For pair ('c', 'C'):

    • 'c' in s? ✓ (Yes)
    • 'C' in s? ✓ (Yes)
    • Both exist → 'c' is special (count = 3)
  • For pair ('d', 'D'):

    • 'd' in s? ✗ (No)
    • 'D' in s? ✗ (No)
    • Neither exist → 'd' is not special
  • ... (continuing for remaining letters e-z, none of which appear in our string)

Step 3: Return the count

The sum function counts all the True values (where both lowercase and uppercase exist):

  • Letters a, b, and c are special
  • Total count = 3

Therefore, the function returns 3 as the number of special letters in "aaAbcBC".

Solution Implementation

1class Solution:
2    def numberOfSpecialChars(self, word: str) -> int:
3        """
4        Count the number of special characters where both lowercase and uppercase versions exist.
5      
6        A character is considered special if both its lowercase and uppercase forms
7        appear in the given word.
8      
9        Args:
10            word: Input string to check for special characters
11          
12        Returns:
13            Number of special character pairs found in the word
14        """
15        # Import required constants for ASCII letters
16        from string import ascii_lowercase, ascii_uppercase
17      
18        # Convert word to set for O(1) lookup of characters
19        char_set = set(word)
20      
21        # Count pairs where both lowercase and uppercase versions exist
22        # zip pairs each lowercase letter with its corresponding uppercase letter
23        special_count = sum(
24            lower_char in char_set and upper_char in char_set 
25            for lower_char, upper_char in zip(ascii_lowercase, ascii_uppercase)
26        )
27      
28        return special_count
29
1class Solution {
2    public int numberOfSpecialChars(String word) {
3        // Create a boolean array to track presence of characters
4        // Size is 'z' + 1 (123) to accommodate all uppercase and lowercase letters
5        boolean[] characterPresence = new boolean['z' + 1];
6      
7        // Iterate through each character in the word
8        for (int i = 0; i < word.length(); i++) {
9            // Mark the character as present using its ASCII value as index
10            characterPresence[word.charAt(i)] = true;
11        }
12      
13        // Initialize counter for special characters
14        int specialCharCount = 0;
15      
16        // Check each letter from 'a' to 'z' (26 letters total)
17        for (int i = 0; i < 26; i++) {
18            // A character is special if both its lowercase and uppercase forms exist
19            if (characterPresence['a' + i] && characterPresence['A' + i]) {
20                specialCharCount++;
21            }
22        }
23      
24        // Return the total count of special characters
25        return specialCharCount;
26    }
27}
28
1class Solution {
2public:
3    int numberOfSpecialChars(string word) {
4        // Create a boolean array to track presence of each character
5        // Size is 'z' + 1 to accommodate all ASCII values from 0 to 'z'
6        vector<bool> charPresent('z' + 1, false);
7      
8        // Mark each character in the word as present
9        for (char& ch : word) {
10            charPresent[ch] = true;
11        }
12      
13        // Count letters that appear in both lowercase and uppercase
14        int specialCharCount = 0;
15        for (int i = 0; i < 26; ++i) {
16            // Check if both lowercase and uppercase versions exist
17            if (charPresent['a' + i] && charPresent['A' + i]) {
18                specialCharCount++;
19            }
20        }
21      
22        return specialCharCount;
23    }
24};
25
1/**
2 * Counts the number of special characters in a word.
3 * A character is considered special if both its lowercase and uppercase versions appear in the word.
4 * 
5 * @param word - The input string to analyze
6 * @returns The count of special characters (letters that appear in both cases)
7 */
8function numberOfSpecialChars(word: string): number {
9    // Create a boolean array to track which ASCII characters are present
10    // Array size is 'z' ASCII value + 1 to cover all lowercase letters
11    const characterPresence: boolean[] = Array.from(
12        { length: 'z'.charCodeAt(0) + 1 }, 
13        () => false
14    );
15  
16    // Mark each character in the word as present in the array
17    for (let i = 0; i < word.length; i++) {
18        const asciiCode = word.charCodeAt(i);
19        characterPresence[asciiCode] = true;
20    }
21  
22    // Count letters that appear in both lowercase and uppercase
23    let specialCharCount: number = 0;
24  
25    // Check all 26 letters of the alphabet
26    for (let i = 0; i < 26; i++) {
27        const lowercaseAscii = 'a'.charCodeAt(0) + i;
28        const uppercaseAscii = 'A'.charCodeAt(0) + i;
29      
30        // If both lowercase and uppercase versions are present, increment counter
31        if (characterPresence[lowercaseAscii] && characterPresence[uppercaseAscii]) {
32            specialCharCount++;
33        }
34    }
35  
36    return specialCharCount;
37}
38

Time and Space Complexity

The time complexity is O(n + |Σ|), where n is the length of the string word and |Σ| is the size of the character set. Breaking this down:

  • Creating the set from word takes O(n) time as it needs to iterate through each character
  • The zip(ascii_lowercase, ascii_uppercase) creates 26 pairs, so the loop runs O(26) = O(|Σ|) times
  • Each membership check (in s) takes O(1) average time for a set
  • Therefore, total time complexity is O(n) + O(|Σ|) = O(n + |Σ|)

The space complexity is O(|Σ|), where |Σ| represents the size of the character set. This is because:

  • The set s stores unique characters from word, which can be at most O(|Σ|) characters
  • The zip operation and iteration use constant extra space
  • In this problem, |Σ| ≤ 128 since we're dealing with ASCII characters (both uppercase and lowercase letters)

Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Counting Duplicate Occurrences

A common mistake is counting the same special letter multiple times when it appears in different positions in the string.

Incorrect Approach:

def numberOfSpecialChars(self, word: str) -> int:
    count = 0
    for char in word:
        if char.islower() and char.upper() in word:
            count += 1
        elif char.isupper() and char.lower() in word:
            count += 1
    return count

Problem: If the word is "aAbBcC", this would count 'a' twice (once when encountering 'a' and once when encountering 'A'), resulting in an incorrect count of 6 instead of 3.

Solution: Use a set to track which letters have already been identified as special, or check all 26 letters exactly once as in the correct solution.

Pitfall 2: Case Sensitivity in Set Operations

Another mistake is trying to use case-insensitive comparisons incorrectly.

Incorrect Approach:

def numberOfSpecialChars(self, word: str) -> int:
    lower_chars = set(word.lower())
    upper_chars = set(word.upper())
    # This doesn't work because both sets contain the same letters
    return len(lower_chars & upper_chars)

Problem: Converting the entire word to lowercase or uppercase loses the information about which case versions actually exist in the original string.

Solution: Preserve the original cases by working with the original string's character set.

Pitfall 3: Inefficient Character-by-Character Checking

Checking each character against every other character in the string leads to poor performance.

Incorrect Approach:

def numberOfSpecialChars(self, word: str) -> int:
    special = set()
    for i, char in enumerate(word):
        for j, other in enumerate(word):
            if i != j and char.swapcase() == other:
                special.add(char.lower())
    return len(special)

Problem: This has O(n²) time complexity, which is inefficient for large strings.

Solution: Use the set-based approach with O(n) complexity as shown in the correct solution, checking only the 26 possible letter pairs once.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What are the two properties the problem needs to have for dynamic programming to be applicable? (Select 2)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More