2026-09-20 — First Unique Character in a String
Difficulty: 🟢 Easy Pattern: Hash map (count first, scan second) Time-box: 20 minutes
Problem
Given a string s, return the index (0-based) of the first character that
appears exactly once. If every character repeats, return -1.
You may assume s contains only lowercase letters.
Examples
"leetcode" -> 0 (the 'l' is the first character occurring once)
"loveleetcode" -> 2 ('o' is the first unique letter)
"aabb" -> -1 (no character is unique)
"" -> -1 (empty string)
Constraints
0 <= s.length <= 5 * 10^4sconsists of lowercase English letters.- Scanning the string twice is fine; scanning it once per character is not.
Hint (open only if stuck after 10 minutes)
Count how many times each character occurs — a single pass into a small frequency table. Then make a SECOND pass and return the first index whose character's count is 1. The order of the string only matters in the second pass, which is why "count first, decide later" is the move.
Solution
from collections import Counter
def first_unique(s: str) -> int:
counts = Counter(s) # one pass: how often each letter occurs
for i, ch in enumerate(s): # second pass: first with count 1
if counts[ch] == 1:
return i
return -1
How it works. The decision "is this character unique?" needs global information (the total count), so it cannot be made while you are still reading the string. Collect the counts, then walk the original string — not the count table, which would lose the order — and report the first character whose total is 1.
Complexity. Time: O(n) — two linear passes, O(1) hash work each (alphabet size is bounded at 26). Space: O(1) — the counter holds at most 26 entries regardless of input length.
Edge cases that matter. Empty string (returns -1 without looping). A string of all identical characters. A unique character sitting at the END — the second pass must run to completion before giving up.
Common wrong answer. Tracking a seen set and returning the first
character not in seen — that finds the first character you have not
yet seen, i.e. index 0, always. "Seen once so far" is not "will be seen
exactly once in total"; you need the full count before deciding.
Found a mistake? Edit this page on GitHub.