Skip to main content

387. First Unique Character in a String

easyAsked at Bloomberg

Given a string, find the index of the first non-repeating character, or -1 if all repeat. Bloomberg uses this as a hash-map fluency warm-up — they want a two-pass O(n) with a discussion of LinkedHashMap (or ordered Map) as an alternative.

By Alex Chen, Founder, InterviewChamp.AI · Last verified

Source citations

Public interview reports confirming this problem appears in Bloomberg loops.

  • Glassdoor (2026-Q1)Bloomberg SWE phone-screen reports cite First Unique Character as a recurring string + hash-map warm-up.
  • Blind (2025-12)Bloomberg new-grad reports note this as a common Round 1 question.

Problem

Given a string s, find the first non-repeating character in it and return its index. If it does not exist, return -1.

Constraints

  • 1 <= s.length <= 10^5
  • s consists of only lowercase English letters.

Examples

Example 1

Input
s = "leetcode"
Output
0

Example 2

Input
s = "loveleetcode"
Output
2

Example 3

Input
s = "aabb"
Output
-1

Approaches

1. Two-pass frequency count (optimal)

First pass: count frequency of each char. Second pass: return the index of the first char with count 1.

Time
O(n)
Space
O(1)
function firstUniqChar(s) {
  const count = new Map();
  for (const ch of s) {
    count.set(ch, (count.get(ch) || 0) + 1);
  }
  for (let i = 0; i < s.length; i++) {
    if (count.get(s[i]) === 1) return i;
  }
  return -1;
}

Tradeoff: The canonical two-pass answer. Space is technically O(1) because the alphabet is bounded (26 lowercase letters). Bloomberg's preferred answer.

2. Ordered map with single pass

JavaScript's Map preserves insertion order. Track count + first-seen index; iterate the map at the end for the smallest index with count 1.

Time
O(n)
Space
O(1)
function firstUniqCharSingle(s) {
  const info = new Map();
  for (let i = 0; i < s.length; i++) {
    const ch = s[i];
    if (info.has(ch)) info.set(ch, { count: info.get(ch).count + 1, idx: info.get(ch).idx });
    else info.set(ch, { count: 1, idx: i });
  }
  let best = -1;
  for (const { count, idx } of info.values()) {
    if (count === 1 && (best === -1 || idx < best)) best = idx;
  }
  return best;
}

Tradeoff: Single pass through s but more bookkeeping. The two-pass version is clearer and Bloomberg interviewers tend to prefer it.

Bloomberg-specific tips

Bloomberg interviewers like the two-pass solution because it's clean and easy to defend. State 'first pass counts, second pass scans in order for the first count-1 entry' before coding. Mention the LinkedHashMap variant if they probe for alternatives.

Common mistakes

  • Returning the character instead of the index.
  • Failing to return -1 when no unique character exists.
  • Using nested loops — O(n^2) when O(n) is trivial.

Follow-up questions

An interviewer at Bloomberg may pivot to one of these next:

  • First Unique Number — same shape but on integers.
  • Find the Difference (LC 389) — XOR or frequency.
  • Group Anagrams (LC 49) — frequency-as-key pattern.

Solve it now

Free. No sign-up. Python and JavaScript run instantly in your browser.

Output

Press Run or Cmd+Enter to execute

FAQ

Why is space O(1)?

Because the alphabet is bounded to 26 lowercase letters per the constraints. The map size never exceeds 26 regardless of n.

Stream variant?

Bloomberg sometimes asks 'what if chars stream one at a time and we want the first unique at any moment?' Use an ordered map + sentinel; pop expired uniques from the front.

Free learning resources

Curated free links for this problem.

Practice these live with InterviewChamp.AI

Drill First Unique Character in a String and other Bloomberg interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.

Practice these live with InterviewChamp.AI →