First Unique Character in String

Find the first non-repeating character in a string and return its index.

Language Selection

Choose your preferred programming language

Showing: Python

First Unique Character in String

Problem Statement

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

Examples

Example 1:

Input: s = "leetcode"
Output: 0
Explanation: The character 'l' at index 0 is the first non-repeating character.

Example 2:

Input: s = "loveleetcode"
Output: 2
Explanation: The character 'v' at index 2 is the first non-repeating character.

Example 3:

Input: s = "aabb"
Output: -1
Explanation: All characters repeat, so return -1.

Constraints

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

Approach 1: Two-Pass Hash Map

Algorithm

Use a hash map to count character frequencies, then iterate through the string to find the first character with frequency 1.

Steps:

  1. Count frequency of each character
  2. Iterate through string to find first character with frequency 1
  3. Return its index or -1 if not found

Implementation

Python:

def first_uniq_char(s):
    """
    Find first unique character using hash map
    Time: O(n)
    Space: O(1) - at most 26 characters
    """
    # Count character frequencies
    char_count = {}
    for char in s:
        char_count[char] = char_count.get(char, 0) + 1
    
    # Find first character with frequency 1
    for i, char in enumerate(s):
        if char_count[char] == 1:
            return i
    
    return -1

Java:

class Solution {
    /**
     * Find first unique character using hash map
     * Time: O(n)
     * Space: O(1) - at most 26 characters
     */
    public int firstUniqChar(String s) {
        // Count character frequencies
        Map<Character, Integer> charCount = new HashMap<>();
        for (char c : s.toCharArray()) {
            charCount.put(c, charCount.getOrDefault(c, 0) + 1);
        }
        
        // Find first character with frequency 1
        for (int i = 0; i < s.length(); i++) {
            if (charCount.get(s.charAt(i)) == 1) {
                return i;
            }
        }
        
        return -1;
    }
}

Go:

// firstUniqChar - Find first unique character using hash map
// Time: O(n)
// Space: O(1) - at most 26 characters
func firstUniqChar(s string) int {
    // Count character frequencies
    charCount := make(map[rune]int)
    for _, char := range s {
        charCount[char]++
    }
    
    // Find first character with frequency 1
    for i, char := range s {
        if charCount[char] == 1 {
            return i
        }
    }
    
    return -1
}

JavaScript:

/**
 * Find first unique character using hash map
 * Time: O(n)
 * Space: O(1) - at most 26 characters
 */
function firstUniqChar(s) {
    // Count character frequencies
    const charCount = {};
    for (const char of s) {
        charCount[char] = (charCount[char] || 0) + 1;
    }
    
    // Find first character with frequency 1
    for (let i = 0; i < s.length; i++) {
        if (charCount[s[i]] === 1) {
            return i;
        }
    }
    
    return -1;
}

C#:

public class Solution {
    /// <summary>
    /// Find first unique character using hash map
    /// Time: O(n)
    /// Space: O(1) - at most 26 characters
    /// </summary>
    public int FirstUniqChar(string s) {
        // Count character frequencies
        var charCount = new Dictionary<char, int>();
        foreach (char c in s) {
            charCount[c] = charCount.GetValueOrDefault(c, 0) + 1;
        }
        
        // Find first character with frequency 1
        for (int i = 0; i < s.Length; i++) {
            if (charCount[s[i]] == 1) {
                return i;
            }
        }
        
        return -1;
    }
}

Complexity Analysis

  • Time Complexity: O(n) where n is the length of the string
  • Space Complexity: O(1) since we have at most 26 characters

Approach 2: Array for Character Counting

Algorithm

Use an array of size 26 to count character frequencies instead of a hash map, since we only have lowercase English letters.

Implementation

Python:

def first_uniq_char_array(s):
    """
    Find first unique character using array
    Time: O(n)
    Space: O(1) - fixed size array of 26
    """
    # Count character frequencies using array
    char_count = [0] * 26
    for char in s:
        char_count[ord(char) - ord('a')] += 1
    
    # Find first character with frequency 1
    for i, char in enumerate(s):
        if char_count[ord(char) - ord('a')] == 1:
            return i
    
    return -1

Java:

class Solution {
    /**
     * Find first unique character using array
     * Time: O(n)
     * Space: O(1) - fixed size array of 26
     */
    public int firstUniqCharArray(String s) {
        // Count character frequencies using array
        int[] charCount = new int[26];
        for (char c : s.toCharArray()) {
            charCount[c - 'a']++;
        }
        
        // Find first character with frequency 1
        for (int i = 0; i < s.length(); i++) {
            if (charCount[s.charAt(i) - 'a'] == 1) {
                return i;
            }
        }
        
        return -1;
    }
}

Go:

// firstUniqCharArray - Find first unique character using array
// Time: O(n)
// Space: O(1) - fixed size array of 26
func firstUniqCharArray(s string) int {
    // Count character frequencies using array
    charCount := make([]int, 26)
    for _, char := range s {
        charCount[char-'a']++
    }
    
    // Find first character with frequency 1
    for i, char := range s {
        if charCount[char-'a'] == 1 {
            return i
        }
    }
    
    return -1
}

JavaScript:

/**
 * Find first unique character using array
 * Time: O(n)
 * Space: O(1) - fixed size array of 26
 */
function firstUniqCharArray(s) {
    // Count character frequencies using array
    const charCount = new Array(26).fill(0);
    for (const char of s) {
        charCount[char.charCodeAt(0) - 'a'.charCodeAt(0)]++;
    }
    
    // Find first character with frequency 1
    for (let i = 0; i < s.length; i++) {
        if (charCount[s[i].charCodeAt(0) - 'a'.charCodeAt(0)] === 1) {
            return i;
        }
    }
    
    return -1;
}

C#:

public class Solution {
    /// <summary>
    /// Find first unique character using array
    /// Time: O(n)
    /// Space: O(1) - fixed size array of 26
    /// </summary>
    public int FirstUniqCharArray(string s) {
        // Count character frequencies using array
        int[] charCount = new int[26];
        foreach (char c in s) {
            charCount[c - 'a']++;
        }
        
        // Find first character with frequency 1
        for (int i = 0; i < s.Length; i++) {
            if (charCount[s[i] - 'a'] == 1) {
                return i;
            }
        }
        
        return -1;
    }
}

Complexity Analysis

  • Time Complexity: O(n) where n is the length of the string
  • Space Complexity: O(1) for the fixed-size array

Key Insights

  1. Two-Pass Approach: First pass counts frequencies, second pass finds first unique
  2. Space Optimization: Use array instead of hash map for better performance
  3. Character Mapping: Map characters to array indices using ASCII values
  4. Early Termination: Can stop as soon as first unique character is found

Edge Cases

  1. Single character: String with one character
  2. All unique: Each character appears once
  3. All repeated: Every character appears multiple times
  4. Empty string: Handle empty input (though constraints say length >= 1)
  5. Two characters: Minimum case with two characters

Test Cases

# Test cases
assert first_uniq_char("leetcode") == 0
assert first_uniq_char("loveleetcode") == 2
assert first_uniq_char("aabb") == -1
assert first_uniq_char("a") == 0
assert first_uniq_char("ab") == 0
assert first_uniq_char("aa") == -1
assert first_uniq_char("abcdef") == 0

Follow-up Questions

  1. First Unique Character in Stream: Handle characters arriving one by one
  2. Kth Unique Character: Find the kth non-repeating character
  3. Unique Characters Count: Count total number of unique characters
  4. Remove Duplicates: Remove all duplicate characters from string
  5. Longest Substring with Unique Characters: Find longest substring with all unique characters

Common Mistakes

  1. Index confusion: Returning character instead of index
  2. Case sensitivity: Not handling case differences properly
  3. Empty string: Not handling edge case of empty string
  4. Character vs index: Confusing character value with its index
  5. Frequency counting: Incorrect frequency counting logic

Interview Tips

  1. Clarify requirements: Ask about case sensitivity and character set
  2. Start simple: Begin with hash map approach, then optimize
  3. Discuss trade-offs: Hash map vs array for character counting
  4. Handle edge cases: Always consider single character and all repeated cases
  5. Optimize space: Mention array optimization for lowercase letters only

Variations

  1. Case-insensitive: Ignore case differences
  2. Extended ASCII: Handle extended character set
  3. Unicode: Support Unicode characters
  4. Case-sensitive: Distinguish between uppercase and lowercase
  5. Custom alphabet: Handle custom character sets