242. Valid Anagram
題目 / Problem
中文: 給定兩個字串 s 和 t,如果 t 是 s 的字母異位詞(anagram),回傳 true,否則回傳 false。所謂字母異位詞,是指由完全相同的字母、以完全相同的數量、但可能不同順序排列而成的字串。
English: Given two strings s and t, return true if t is an anagram of s, and false otherwise. An anagram uses exactly the same letters with exactly the same counts, just possibly in a different order.
Constraints / 限制條件:
- 1 <= s.length, t.length <= 5 * 10^4
- s 和 t 只包含小寫英文字母 / s and t consist of lowercase English letters.
Worked example / 範例:
- Input: s = "anagram", t = "nagaram" → Output: true(兩者都由 3 個 a、1 個 n、1 個 g、1 個 r、1 個 m 組成)
- Input: s = "rat", t = "car" → Output: false(s 有 t 但沒有 c)
名詞解釋 / Glossary
- 字母異位詞 / anagram:兩個字串包含完全相同的字母種類與數量,只是排列順序可能不同。例如
"listen"和"silent"。/ Two strings made of the exact same letters in the exact same counts, just reordered. - 雜湊表 / hash map:一種把「鍵 (key)」對應到「值 (value)」的資料結構,查詢與更新平均都是 O(1)。這裡我們用它來記錄每個字母出現幾次。/ A data structure mapping keys to values with average O(1) lookup; here it counts how many times each letter appears.
- 計數陣列 / frequency array:當鍵的範圍很小(例如 26 個小寫字母)時,可以直接用一個固定大小的陣列代替雜湊表,索引就是字母、值就是次數。比雜湊表更快更省。/ When keys are few (e.g. 26 lowercase letters), a fixed-size array replaces the hash map: the index is the letter, the value is its count.
- ASCII 值 / ASCII value:每個字元在電腦裡其實是一個整數編碼。小寫
'a'是 97、'b'是 98……所以c - 'a'會得到 0~25 的索引。/ Each character is stored as an integer code;'a'is 97, soc - 'a'gives an index 0–25. - 時間複雜度 / time complexity:用來描述演算法執行時間如何隨輸入大小成長的記號,例如 O(n) 表示「與 n 成正比」。/ Notation describing how runtime grows with input size; O(n) means proportional to n.
思路
中文:最直覺的暴力解法是把兩個字串都「排序」,如果排序後完全一樣,那它們就是字母異位詞。這個方法正確,但排序需要 O(n log n) 的時間,比必要的慢。更好的觀察是:字母異位詞的本質就是「每個字母出現的次數都一樣」。所以我們不需要在乎順序,只要數一數每個字母出現幾次就好。由於題目保證只有 26 個小寫字母,我們可以開一個大小為 26 的計數陣列 count,索引 c - 'a' 對應字母 c。第一步:先檢查兩字串長度,如果長度不同,絕對不可能是異位詞,直接回傳 false(這也是一個很有用的提早剪枝)。第二步:掃過 s,把每個字母的計數 +1;同時掃過 t,把每個字母的計數 -1。如果 s 和 t 的字母組成完全相同,那麼每個字母被加的次數會剛好等於被減的次數,最後整個 count 陣列會全部歸零。第三步:檢查 count 是否全為 0,只要有任何一格不是 0,就代表某個字母在兩邊數量不一致,回傳 false。這個方法只掃過字串常數次,時間是 O(n),而陣列固定 26 格,額外空間是 O(1)。
English: The most obvious brute force is to sort both strings and compare — if the sorted versions are identical, they're anagrams. That works but sorting costs O(n log n), more than we need. The key insight is that being an anagram means every letter appears the same number of times in both strings; order is irrelevant. So instead of sorting, we just count letters. Because the input is limited to 26 lowercase letters, we use a fixed count array of size 26, where index c - 'a' represents letter c. Step one: if the two lengths differ, they can't be anagrams — return false immediately (a cheap, useful early exit). Step two: walk through s adding 1 to each letter's count, and walk through t subtracting 1. If the letter compositions match exactly, every increment is cancelled by a matching decrement, so the whole array ends at zero. Step three: scan the array; if any slot is non-zero, some letter's counts didn't match, so return false. We only pass over the strings a constant number of times — O(n) time — and the array is a fixed 26 slots, so O(1) extra space.
逐步走查 / Walkthrough
以 s = "anagram", t = "nagaram" 為例 / Using s = "anagram", t = "nagaram":
長度檢查 / Length check:s 長度 7,t 長度 7,相等,繼續 / equal, continue.
掃描 s(每個字母 count[c-'a'] += 1)/ Scan s (increment):
| 字元 / char | 動作 / action | 受影響的計數 / affected count |
|---|---|---|
a |
count[0] += 1 | a=1 |
n |
count[13] += 1 | a=1, n=1 |
a |
count[0] += 1 | a=2, n=1 |
g |
count[6] += 1 | a=2, n=1, g=1 |
r |
count[17] += 1 | a=2, n=1, g=1, r=1 |
a |
count[0] += 1 | a=3, n=1, g=1, r=1 |
m |
count[12] += 1 | a=3, n=1, g=1, r=1, m=1 |
掃描 t(每個字母 count[c-'a'] -= 1)/ Scan t (decrement):
| 字元 / char | 動作 / action | a / n / g / r / m 計數 |
|---|---|---|
n |
count[13] -= 1 | a=3, n=0, g=1, r=1, m=1 |
a |
count[0] -= 1 | a=2, n=0, g=1, r=1, m=1 |
g |
count[6] -= 1 | a=2, n=0, g=0, r=1, m=1 |
a |
count[0] -= 1 | a=1, n=0, g=0, r=1, m=1 |
r |
count[17] -= 1 | a=1, n=0, g=0, r=0, m=1 |
a |
count[0] -= 1 | a=0, n=0, g=0, r=0, m=1 |
m |
count[12] -= 1 | a=0, n=0, g=0, r=0, m=0 |
最終檢查 / Final check:所有 26 格都是 0 → 回傳 true / all slots are 0 → return true. ✅
Solution — C
// 演算法 / Algorithm:
// 用大小 26 的計數陣列,掃 s 時每個字母 +1,掃 t 時 -1;
// 若兩字串是異位詞,計數最終全部歸零。長度不同則直接 false。
// Use a size-26 count array: +1 for each letter of s, -1 for each of t.
// If they're anagrams every count returns to 0. Different lengths => false.
#include <string.h> // 為了 strlen / for strlen
#include <stdbool.h> // 為了 bool / true / false 型別 / for the bool type
bool isAnagram(char* s, char* t) {
// strlen 回傳字串長度(不含結尾的 '