The problem. Given a string s of lowercase letters, find the first character that appears exactly once and return its index. If there is none, return -1.
s = "leetcode" -> 0 ("l" appears once)
s = "loveleetcode" -> 2 ("v")
s = "aabb" -> -1Whether a letter is unique depends on the whole string — the second copy might be the very last character. So one pass can't decide as it goes. Two passes can: the first counts every letter, the second walks the string in order and returns the first position whose letter has count 1. The counts turn "does this appear anywhere else?" from a search into an O(1) lookup.
function firstUniqChar(s) { for (let i = 0; i < s.length; i++) { let repeated = false; for (let j = 0; j < s.length; j++) { if (i !== j && s[i] === s[j]) { // the same letter somewhere else repeated = true; break; } } if (!repeated) return i; } return -1;}Time O(n²), space O(1). Every letter may be compared with every other one.
Pass 1 fills the 26 counters; pass 2 stops at the first letter whose counter is 1.
function firstUniqChar(s) { const count = new Array(26).fill(0); for (const ch of s) count[ch.charCodeAt(0) - 97]++; // pass 1: count every letter for (let i = 0; i < s.length; i++) { if (count[s.charCodeAt(i) - 97] === 1) return i; // pass 2: the first letter seen once } return -1;}Time O(n), space O(1). Two passes over the string; the 26 counters don't grow with the input.
For a stream of characters you can't scan twice: keep the counts plus a queue of candidates in arrival order, and pop from the front while its count is above 1. The front of the queue is always the current first unique character.
"aabb": -1."leetcode": 0."abca": a is not unique, so the answer is 1 (b).t is the counter that doesn't balance.