← 題庫 / Archive
2026-05-26 Daily Easy Hash TableString

3120. Count the Number of Special Characters I

題目 / Problem

給你一個字串 word。如果某個字母同時以小寫和大寫出現在 word 中,這個字母就被稱為特殊字母(special)。請回傳 word 中特殊字母的數量。

You are given a string word. A letter is called special if it appears both in lowercase and uppercase in word. Return the number of special letters in word.

約束 / Constraints: - 1 <= word.length <= 50 - word 只由大小寫英文字母組成 / word consists of only lowercase and uppercase English letters.

範例 / Example: word = "aaAbcBC" → 輸出 3。因為 'a'(有 aA)、'b'(有 bB)、'c'(有 cC)三個字母都同時出現了大小寫,所以答案是 3。

名詞解釋 / Glossary

  • ASCII 值 / ASCII value:每個字元在電腦裡其實是一個整數。例如 'a' 是 97,'A' 是 65。把字元當數字用,就能做減法算出它是第幾個字母。 / Every character is secretly an integer. 'a' is 97, 'A' is 65. Treating chars as numbers lets us index arrays.
  • 布林陣列 / boolean array:一個只存「真/假」的陣列。我們用 seenLower[26] 記錄 26 個小寫字母各自有沒有出現過。 / An array storing only true/false. We use it to record whether each of the 26 letters has appeared.
  • 字元映射成索引 / mapping a char to an index:用 c - 'a''a'..'z' 變成 0..25,這樣就能當陣列下標使用。 / Use c - 'a' to turn 'a'..'z' into 0..25 so it can be an array subscript.
  • 原地遍歷 / single pass:只把字串從頭到尾掃一遍,不需要巢狀迴圈。 / Scanning the string once, front to back, with no nested loop.

思路

最直接的暴力想法是:對 26 個字母中的每一個,掃一遍字串看看小寫版本在不在,再掃一遍看看大寫版本在不在,兩個都在就計數加一。因為字串長度最多只有 50,這樣做 26 × 50 × 2 次完全沒問題,但它重複掃描了很多次,不夠優雅。更好的做法是反過來:只掃字串一次,邊掃邊記錄「我看過哪些小寫字母」和「我看過哪些大寫字母」。我們開兩個長度 26 的布林陣列 seenLowerseenUpper,遇到小寫字母 c 就把 seenLower[c-'a'] 設為真,遇到大寫字母就把 seenUpper[c-'A'] 設為真。掃完之後,再從 0 到 25 檢查每個字母編號 i:如果 seenLower[i]seenUpper[i] 同時為真,代表這個字母大小寫都出現過,就是特殊字母,計數加一。這裡的關鍵是用「字母編號」(0..25)把同一個字母的大寫和小寫對齊到同一個位置——'a''A' 都對應編號 0,所以我們能在最後一步比對它們。

The brute-force idea is: for each of the 26 letters, scan the string to see if its lowercase form exists, scan again for its uppercase form, and count it if both are present. Since the string is at most 50 characters, doing 26 × 50 × 2 checks is fine, but it rescans the string wastefully. A cleaner approach flips it around: scan the string just once while recording which lowercase letters and which uppercase letters we have seen. We keep two boolean arrays of length 26, seenLower and seenUpper. For a lowercase char c we set seenLower[c-'a'] = true; for an uppercase char we set seenUpper[c-'A'] = true. After the pass, we loop i from 0 to 25 and count every i where both seenLower[i] and seenUpper[i] are true — that letter appears in both cases, so it is special. The trick is using the letter's index (0..25) to align the uppercase and lowercase versions of the same letter to the same slot: both 'a' and 'A' map to index 0, letting us compare them at the end.

逐步走查 / Walkthrough

輸入 / Input: word = "aaAbcBC"。兩個陣列一開始全是 false。逐字處理 / Process char by char:

步驟 / Step 字元 / Char 動作 / Action seenLower(被設真的位置) seenUpper(被設真的位置)
1 a 小寫,設 seenLower[0]=true {a} {}
2 a 小寫,seenLower[0] 已是 true {a} {}
3 A 大寫,設 seenUpper[0]=true {a} {A}
4 b 小寫,設 seenLower[1]=true {a,b} {A}
5 c 小寫,設 seenLower[2]=true {a,b,c} {A}
6 B 大寫,設 seenUpper[1]=true {a,b,c} {A,B}
7 C 大寫,設 seenUpper[2]=true {a,b,c} {A,B,C}

最後比對 / Final comparison(檢查每個編號 i 是否兩邊都為真): - i=0(字母 a):lower ✓、upper ✓ → 特殊,count=1 - i=1(字母 b):lower ✓、upper ✓ → 特殊,count=2 - i=2(字母 c):lower ✓、upper ✓ → 特殊,count=3 - i=3..25:兩邊皆 false → 跳過

答案 / Answer: 3

Solution — C

// 演算法 / Algorithm:
// 掃字串一次,用兩個布林陣列分別記下出現過的小寫與大寫字母;
// 最後數出有幾個字母編號同時在兩個陣列中為真。
// Scan once, mark seen lowercase/uppercase letters in two boolean arrays,
// then count letter indices that are true in BOTH arrays.

int numberOfSpecialChars(char* word) {
    bool seenLower[26] = {false};   // 26 個小寫字母看過沒 / has each lowercase letter been seen
    bool seenUpper[26] = {false};   // 26 個大寫字母看過沒 / has each uppercase letter been seen

    // 從頭掃到字串結尾的 ''(C 字串以 '' 結束)
    // Walk until the terminating '' (C strings end with '')
    for (int i = 0; word[i] != ''; i++) {
        char c = word[i];                  // 取出目前字元 / current character
        if (c >= 'a' && c <= 'z')          // 是小寫字母嗎 / is it lowercase?
            seenLower[c - 'a'] = true;     // c-'a' 把 a..z 變成 0..25 並標記 / map a..z to 0..25 and mark
        else                               // 否則一定是大寫(題目保證只有字母)/ otherwise uppercase (input is all letters)
            seenUpper[c - 'A'] = true;     // c-'A' 把 A..Z 變成 0..25 並標記 / map A..Z to 0..25 and mark
    }

    int count = 0;                         // 特殊字母計數 / number of special letters
    for (int i = 0; i < 26; i++)           // 檢查 26 個字母編號 / check all 26 letter indices
        if (seenLower[i] && seenUpper[i])  // 同一字母大小寫都出現過 / both cases of this letter appeared
            count++;                       // 是特殊字母,加一 / it is special, count it

    return count;                          // 回傳結果 / return the answer
}

Solution — C++

// 演算法 / Algorithm:
// 與 C 版相同:掃一次字串記錄出現過的大小寫字母,
// 最後統計同時擁有大小寫兩種形式的字母個數。
// Same as the C version: one pass to record seen letters, then count
// letters that appear in both lowercase and uppercase.
class Solution {
public:
    int numberOfSpecialChars(string word) {
        // vector<bool> 是會自動管理記憶體的布林陣列;初值全部設為 false
        // vector<bool> is a self-managing boolean array; all elements start as false
        vector<bool> seenLower(26, false);
        vector<bool> seenUpper(26, false);

        // range-for:依序取出 word 裡的每個字元,比手動索引更簡潔
        // range-for: iterate each character of word in order, cleaner than manual indexing
        for (char c : word) {
            if (islower(c))                  // <cctype> 的函式,判斷是否為小寫 / from <cctype>, true if lowercase
                seenLower[c - 'a'] = true;   // 標記這個小寫字母 / mark this lowercase letter
            else                             // 其餘為大寫 / the rest are uppercase
                seenUpper[c - 'A'] = true;   // 標記這個大寫字母 / mark this uppercase letter
        }

        int count = 0;                       // 特殊字母計數 / count of special letters
        for (int i = 0; i < 26; i++)         // 逐一檢查 26 個字母 / check each of the 26 letters
            if (seenLower[i] && seenUpper[i])// 大小寫都出現 → 特殊 / both cases present → special
                count++;

        return count;                        // 回傳特殊字母數量 / return how many are special
    }
};

複雜度 / Complexity

  • Time: O(n) — n 是字串長度。我們只掃字串一次(n 步),最後固定檢查 26 個位置(常數),所以由掃描字串主導,總體與 n 成正比。 / n is the string length. We scan the string once (n steps), then do a fixed 26-slot check (constant), so the scan dominates and time is linear in n.
  • Space: O(1) — 兩個陣列大小固定為 26,跟輸入長度無關,所以是常數額外空間。 / The two arrays are always size 26 regardless of input length, so the extra space is constant.

Pitfalls & Edge Cases

  • 大小寫對齊用的基準字元不同 / Different base chars for the two cases:小寫要用 c - 'a',大寫要用 c - 'A'。如果兩邊都減 'a',大寫字母會算出負的索引而越界。 / Lowercase must subtract 'a', uppercase must subtract 'A'. Subtracting 'a' from an uppercase char gives a negative, out-of-bounds index.
  • 比對時要比同一個編號 / Compare the same index on both sides:必須是 seenLower[i] && seenUpper[i](同一個 i),這樣才確定是同一個字母的大小寫,而不是不同字母。 / You must check seenLower[i] && seenUpper[i] with the same i, otherwise you'd be matching different letters.
  • 沒有大寫(或沒有小寫)的情況 / No uppercase (or no lowercase):像 "abc" 這種全小寫的輸入,seenUpper 全是 false,答案自然是 0——程式不需要特判,邏輯已自動涵蓋。 / For all-lowercase input like "abc", seenUpper stays all-false and the answer is 0 — no special case needed.
  • 重複字母不會重複計數 / Duplicates don't inflate the count"aaAbcBC" 裡有兩個 a,但布林陣列只記「有沒有」,不記次數,所以 a 只會被算一次。 / Booleans record presence, not frequency, so a repeated letter is counted at most once.