LeetCode #387 Easy

First Unique Character in a String

First Unique Character in a String: return the index of the first character that appears exactly once, or -1 if every character repeats.

Constraints
  • 1 <= s.length <= 10⁵
  • s consists of only lowercase English letters
hash tablestringqueuecounting
Open on LeetCode ↗
First Unique Character in a String diagramA labelled diagram of the structure this problem turns on.pass 1 counts; pass 2 walks the string in ordercountsl:1e:3t:1c:1o:2d:1stringleetcodeidx 0idx 1idx 2idx 3idx 4idx 5idx 6idx 7index 0 is the first character whose count is 1 — the scan walks thestring, not the map, so the index comes for free
02

Intuition

Uniqueness is a property of the whole string, so it cannot be decided while first reading a character — a later duplicate may still appear. That forces two passes: count every character, then walk the string again in order and return the first index whose count is 1. The second pass must follow the original order, because the answer is the first such character, not any of them.

How to spot this pattern

Any question asking for the first or last element with a global property needs the property computed first and the order applied second. The tell is that a decision at index i depends on data after i. Find the Difference and Sort Characters By Frequency share the counting step.

03

Approach

Try it first

Before reading on: explain why the answer cannot be decided during a single left-to-right pass. Then work out why the second pass must walk the string rather than the frequency map.

1

Why one pass cannot work

At the moment you read index 0 you cannot know whether that character recurs at index 500. Any single-pass attempt must therefore either look ahead — which is the quadratic scan in disguise — or defer the decision. Counting first resolves this: after the counting pass every character's total is final, so the second pass can decide each index in constant time. The two passes together are still O(n), which is why this beats the nested search.

2

Counting, then scanning in order

Build a frequency map of all characters in one pass. Then iterate the string by index and return the first i where count[s[i]] == 1. Iterating the map instead of the string is a subtle error: dictionaries preserve insertion order in modern Python, which makes it appear to work, but the value needed is the index, and recovering it with s.index(ch) adds a linear search per candidate. Scanning the string directly gives the index for free and keeps the pass linear.

3

Bounding the space by the alphabet

The constraints say the string is lowercase English letters, so the map holds at most 26 entries regardless of input length — making the space O(1) rather than O(n). A fixed 26-slot array indexed by ord(c) - ord('a') makes that explicit and is what the C++ and Java versions use; it also avoids hashing entirely, so lookups are a single array access. Time is O(n) for the two passes, with the second usually stopping early.

04

Solution & live demo

1from collections import Counter
2 
3 
4class Solution:
5 def firstUniqChar(self, s):
6 counts = Counter(s)
7 for i, ch in enumerate(s):
8 if counts[ch] == 1:
9 return i
10 return -1
05

Common pitfalls

Searching for each character's other occurrence

✗ Wrong
for i, ch in enumerate(s):
    if ch not in s[i+1:] and ch not in s[:i]:
        return i
✓ Right
counts = Counter(s)
for i, ch in enumerate(s):
    if counts[ch] == 1:
        return i

Each slice copies part of the string and scans it, giving O(n²) time and O(n) extra memory per iteration. On a 10⁵-character input this times out.

Iterating the frequency map instead of the string

✗ Wrong
for ch, n in counts.items():
    if n == 1:
        return s.index(ch)
✓ Right
for i, ch in enumerate(s):
    if counts[ch] == 1:
        return i

This relies on dictionary insertion order to be correct at all, and s.index performs a fresh linear search for every candidate. Scanning the string yields the index directly.

Returning the character rather than its index

✗ Wrong
return ch
✓ Right
return i

The problem asks for the position. Returning the character itself type-checks in Python and fails silently against the expected output.

06

Edge cases

Every character repeats, e.g. "aabb"

No count equals 1, so the loop finishes and -1 is returned.

Single character

Its count is 1, so index 0 is the answer.

The unique character is last

The scan runs the full length before finding it.

First character is unique

The scan returns 0 immediately on the second pass.

All characters distinct

Index 0 qualifies, so the scan stops at once.

07

Complexity

Time
O(n)
Space
O(1)
Two linear passes. The 26-slot count array makes the space constant regardless of input length.