Problem
Given a string, find the first character that appears exactly once and return its index. If every character appears more than once, return -1.
- Input:
s = "leetcode" - Output:
0 - Explanation:
'l'appears only once and is at index 0, so we return 0.
Counter-example: s = "aabb" โ output -1, because both 'a' and 'b' appear twice.
Intuition
A character is "unique" only if it appears exactly once across the whole string โ so we must know every character's total count before we can say anything is unique. That naturally suggests two passes: count first, then find the first character whose count is exactly 1. The "first" constraint means we must scan the original string left to right in the second pass, not just inspect the counter.
Approach 1 โ Brute Force (Nested Scan)
For each character, scan the rest of the string to see if it appears anywhere else. Return the index of the first character with no match.
- For each index
iin the string, setis_unique = True. - For each other index
j(wherej โ i), check ifs[i] == s[j]. - If a match is found, mark as not unique and move on to the next
i. - If no match was found after checking all
j, returni. - If no unique character exists, return
-1.
1def firstUniqChar(s: str) -> int:
2 for i in range(len(s)):
3 is_unique = True
4 for j in range(len(s)):
5 if i != j and s[i] == s[j]: # found the same character at a different position
6 is_unique = False
7 break
8 if is_unique:
9 return i
10 return -1Time: O(nยฒ) โ for each of n characters, we scan up to n other positions.
Space: O(1) โ no extra data structures used.
Approach 2 โ Frequency Count (Optimal)
Count the frequency of every character in one pass, then scan the string a second time to find the first character whose count is exactly 1.
- Build a frequency map by iterating through the entire string.
- Iterate through the string a second time, in left-to-right order.
- For each character, check its count in the frequency map.
- Return the index of the first character with count exactly 1.
- If no such character is found, return
-1.
1def firstUniqChar(s: str) -> int:
2 char_count = {}
3 for char in s:
4 char_count[char] = char_count.get(char, 0) + 1 # tally each character's total appearances
5
6 for index, char in enumerate(s):
7 if char_count[char] == 1: # first position in the string where count is exactly 1
8 return index
9 return -1Time: O(n) โ two linear passes over a string of length n.
Space: O(1) โ the frequency array is always size 26, independent of input length.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Brute Force (Nested Scan) | O(nยฒ) | O(1) | Only acceptable for tiny strings; easy to reason about |
| Frequency Count (Optimal) | O(n) | O(1) | Always โ two clean passes, constant extra space |
Common Mistakes
- Returning the character instead of its index โ the problem asks for the position of the unique character, not the character itself.
return charwhen the count is 1 is wrong;return indexis correct. - Missing the
i != jguard in the brute force โ without it,s[i] == s[j]is trivially true wheni == j, making every character appear to have a duplicate of itself. - Using a set instead of a counter โ a set records whether a character appeared at all, but not how many times. You need counts to distinguish "appeared once" from "appeared twice or more."
- Scanning the dictionary in the second pass instead of the string โ always iterate the original string left to right in the second pass. Scanning the dictionary can give the wrong order because dictionary iteration order reflects insertion order, not the order characters first appear in the string.
- Omitting
return -1โ inputs like"aabb"have no unique character. Without a fallback return, the function returnsNone/nullinstead of-1, which fails the check.
Related Problems
longest-substring-without-repeating-charactersโ tracks character presence in a sliding window to enforce uniquenessvalid-anagramโ frequency counting to compare whether two strings use the same charactersfind-all-anagrams-in-a-stringโ sliding window with a character frequency map to find matching windowsisomorphic-stringsโ maps characters to track consistent one-to-one substitutiontop-k-frequent-wordsโ extends frequency counting with ordering and ranking requirements