Facebook Pixel

1805. Number of Different Integers in a String

EasyHash TableString
Leetcode Link

Problem Description

You are given a string word containing digits and lowercase English letters. Your task is to:

  1. Replace every non-digit character with a space
  2. Count how many different integers remain after this replacement

For example, given "a123bc34d8ef34":

  • After replacing letters with spaces: " 123 34 8 34"
  • The integers found are: "123", "34", "8", and "34"
  • The different integers are: "123", "34", and "8" (the second "34" is a duplicate)
  • So the answer would be 3

Important considerations:

  • Leading zeros should be ignored when determining if two integers are the same
  • For example, "01" and "1" represent the same integer
  • The integers are identified by being separated by at least one space (or non-digit character in the original string)

The solution uses a two-pointer approach to identify each integer substring in the original string. For each digit sequence found:

  • It skips any leading zeros
  • Extracts the integer substring (without leading zeros)
  • Adds it to a set to automatically handle duplicates
  • Returns the size of the set, which represents the count of different integers

The time complexity is O(n) where n is the length of the string, as we traverse the string once. The space complexity is O(n) in the worst case for storing the unique integer substrings in the set.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we need to identify and count unique integers that are "hidden" within a string mixed with letters. Rather than actually replacing characters with spaces and then parsing the result, we can directly scan through the original string and extract integers as we encounter them.

When we think about finding integers in the string, we realize that an integer is simply a continuous sequence of digits. Any non-digit character acts as a separator. So our main task becomes: find all continuous digit sequences and count the unique ones.

The challenge with uniqueness comes from leading zeros. Since "01" and "1" represent the same integer, we need to normalize our representation. The natural approach is to skip leading zeros before storing each integer string.

Using a set data structure is the natural choice here because:

  1. It automatically handles duplicates for us
  2. We can store the normalized string representation directly without converting to actual integers (which might overflow)

The two-pointer technique emerges naturally when we think about how to extract these digit sequences:

  • When we encounter a digit, we need to find where this sequence ends
  • Before recording the sequence, we skip any leading zeros
  • We then extract the substring from the first non-zero digit to the end of the sequence

This approach avoids the need to actually perform string replacement operations and directly solves the core problem: finding and counting unique integer substrings while handling the leading zeros edge case.

Solution Approach

The solution uses a two-pointer technique combined with a hash set to find and count unique integers in the string.

Algorithm Steps:

  1. Initialize: Create an empty set s to store unique integer strings, and set pointer i = 0 to traverse the string.

  2. Main traversal loop: While i < n (where n is the length of the string):

    • Check if the current character word[i] is a digit
    • If not a digit, increment i and continue to the next character
  3. Process integer sequences: When we encounter a digit:

    • Skip leading zeros: Use a while loop to advance i past all consecutive '0' characters

      while i < n and word[i] == '0':
          i += 1
    • Find the end of the integer: Set j = i and advance j to find where the digit sequence ends

      j = i
      while j < n and word[j].isdigit():
          j += 1
    • Extract and store: Add the substring word[i:j] to the set

      • This substring represents the integer without leading zeros
      • If i == j, it means the number was all zeros, and we add an empty string (representing "0")
    • Update pointer: Set i = j to continue from where the integer ended

  4. Return result: The size of the set len(s) gives us the count of unique integers

Example Walkthrough:

For input "a01bc01":

  • At position 0: 'a' is not a digit, skip
  • At position 1: '0' is a digit
    • Skip the leading zero at position 1
    • Find '1' at position 2, which is also a digit
    • Extract substring from position 2 to 3: "1"
    • Add "1" to the set
  • Continue through 'b' and 'c'
  • At position 5: '0' is a digit
    • Skip the leading zero at position 5
    • Find '1' at position 6
    • Extract substring: "1"
    • Add "1" to the set (duplicate, so set remains unchanged)
  • Result: set contains {"1"}, so return 1

The beauty of this approach is that it handles leading zeros naturally by skipping them before extraction, and the set automatically eliminates duplicates without any additional logic.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with the string "a12b008c8d008":

Initial state:

  • String: "a12b008c8d008"
  • Set s = {} (empty)
  • Pointer i = 0

Step 1: i = 0, character is 'a'

  • Not a digit, increment i to 1

Step 2: i = 1, character is '1'

  • It's a digit!
  • Skip leading zeros: '1' is not '0', so stay at i = 1
  • Find end of integer: j moves from 1 → 2 → 3 (stops at 'b')
  • Extract substring word[1:3] = "12"
  • Add "12" to set: s = {"12"}
  • Update i = 3

Step 3: i = 3, character is 'b'

  • Not a digit, increment i to 4

Step 4: i = 4, character is '0'

  • It's a digit!
  • Skip leading zeros: advance i from 4 → 5 → 6 (stop at '8')
  • Find end of integer: j moves from 6 → 7 (stops at 'c')
  • Extract substring word[6:7] = "8"
  • Add "8" to set: s = {"12", "8"}
  • Update i = 7

Step 5: i = 7, character is 'c'

  • Not a digit, increment i to 8

Step 6: i = 8, character is '8'

  • It's a digit!
  • Skip leading zeros: '8' is not '0', so stay at i = 8
  • Find end of integer: j moves from 8 → 9 (stops at 'd')
  • Extract substring word[8:9] = "8"
  • Add "8" to set: s = {"12", "8"} (duplicate, set unchanged)
  • Update i = 9

Step 7: i = 9, character is 'd'

  • Not a digit, increment i to 10

Step 8: i = 10, character is '0'

  • It's a digit!
  • Skip leading zeros: advance i from 10 → 11 → 12 (stop at '8')
  • Find end of integer: j moves from 12 → 13 (end of string)
  • Extract substring word[12:13] = "8"
  • Add "8" to set: s = {"12", "8"} (duplicate, set unchanged)
  • Update i = 13

Final Result:

  • Set contains: {"12", "8"}
  • Return len(s) = 2

The algorithm correctly identified three occurrences of "8" (one as "8" and two as "008"), but counted them as one unique integer after removing leading zeros.

Solution Implementation

1class Solution:
2    def numDifferentIntegers(self, word: str) -> int:
3        """
4        Count the number of different integers in a string.
5        Integers are extracted from the string and leading zeros are removed.
6      
7        Args:
8            word: Input string containing digits and non-digit characters
9          
10        Returns:
11            Number of unique integers found in the string
12        """
13        # Set to store unique integer strings
14        unique_integers = set()
15      
16        # Initialize pointers
17        index = 0
18        string_length = len(word)
19      
20        # Traverse the entire string
21        while index < string_length:
22            # Check if current character is a digit
23            if word[index].isdigit():
24                # Skip leading zeros
25                while index < string_length and word[index] == '0':
26                    index += 1
27              
28                # Mark the start of the actual number (after leading zeros)
29                start_index = index
30              
31                # Find the end of the current number
32                while index < string_length and word[index].isdigit():
33                    index += 1
34              
35                # Add the number (without leading zeros) to the set
36                # If all digits were zeros, this adds an empty string
37                unique_integers.add(word[start_index:index])
38            else:
39                # Move to next character if current is not a digit
40                index += 1
41      
42        # Return the count of unique integers
43        return len(unique_integers)
44
1class Solution {
2    public int numDifferentIntegers(String word) {
3        // Use a HashSet to store unique integer strings
4        Set<String> uniqueIntegers = new HashSet<>();
5        int length = word.length();
6      
7        // Iterate through each character in the word
8        for (int i = 0; i < length; ++i) {
9            // Check if current character is a digit
10            if (Character.isDigit(word.charAt(i))) {
11                // Skip leading zeros
12                while (i < length && word.charAt(i) == '0') {
13                    ++i;
14                }
15              
16                // Mark the start position of the non-zero integer
17                int startIndex = i;
18              
19                // Find the end of the current integer sequence
20                while (startIndex < length && Character.isDigit(word.charAt(startIndex))) {
21                    ++startIndex;
22                }
23              
24                // Extract the integer substring (without leading zeros)
25                // and add it to the set
26                uniqueIntegers.add(word.substring(i, startIndex));
27              
28                // Move the main iterator to the end of current integer
29                i = startIndex;
30            }
31        }
32      
33        // Return the count of unique integers found
34        return uniqueIntegers.size();
35    }
36}
37
1class Solution {
2public:
3    int numDifferentIntegers(string word) {
4        // Use unordered set to store unique integer strings
5        unordered_set<string> uniqueNumbers;
6        int length = word.size();
7      
8        // Iterate through each character in the word
9        for (int i = 0; i < length; ++i) {
10            // Check if current character is a digit
11            if (isdigit(word[i])) {
12                // Skip leading zeros
13                while (i < length && word[i] == '0') {
14                    ++i;
15                }
16              
17                // Mark the start position of the number (after leading zeros)
18                int startPos = i;
19              
20                // Find the end position of the current number
21                while (startPos < length && isdigit(word[startPos])) {
22                    ++startPos;
23                }
24              
25                // Extract the number substring and add to set
26                // If all digits were zeros, this will add an empty string
27                uniqueNumbers.insert(word.substr(i, startPos - i));
28              
29                // Move index to the end of current number
30                i = startPos;
31            }
32        }
33      
34        // Return the count of unique integers found
35        return uniqueNumbers.size();
36    }
37};
38
1/**
2 * Counts the number of different integers in a string
3 * @param word - The input string containing digits and letters
4 * @returns The count of unique integers found in the string
5 */
6function numDifferentIntegers(word: string): number {
7    // Replace all non-digit characters with spaces to separate numbers
8    const numbersWithSpaces: string = word.replace(/\D+/g, ' ');
9  
10    // Remove leading and trailing spaces
11    const trimmedString: string = numbersWithSpaces.trim();
12  
13    // Split the string by spaces to get individual number strings
14    const numberStrings: string[] = trimmedString.split(' ');
15  
16    // Filter out empty strings that may result from consecutive spaces
17    const nonEmptyNumbers: string[] = numberStrings.filter((value: string) => value !== '');
18  
19    // Remove leading zeros from each number string to normalize them
20    // (e.g., "001" becomes "1", "000" becomes "")
21    const normalizedNumbers: string[] = nonEmptyNumbers.map((value: string) => {
22        // Remove all leading zeros, empty string remains empty
23        return value.replace(/^0+/g, '') || '0';
24    });
25  
26    // Create a Set to automatically remove duplicates and return its size
27    const uniqueNumbers: Set<string> = new Set(normalizedNumbers);
28  
29    return uniqueNumbers.size;
30}
31

Time and Space Complexity

Time Complexity: O(n), where n is the length of the string word.

The algorithm iterates through the string once using a while loop. In the worst case:

  • The outer while loop runs at most n times
  • The inner while loops (for skipping leading zeros and collecting digits) together process each character at most once
  • Each character is visited a constant number of times throughout the entire execution
  • String slicing word[i:j] takes O(j-i) time, but since we process each character once across all slicing operations, the total time for all slicing operations is O(n)
  • Adding to a set is O(1) on average

Therefore, the overall time complexity is O(n).

Space Complexity: O(n), where n is the length of the string word.

The space usage comes from:

  • The set s which stores unique integer substrings. In the worst case (e.g., "1a2b3c4d5"), we might store O(n) different numbers
  • Each substring stored in the set can be up to O(n) characters long in the worst case (e.g., one long number)
  • The total space used by all substrings combined cannot exceed O(n) characters since they are all substrings of the original string

Therefore, the overall space complexity is O(n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Incorrect Handling of Numbers That Are All Zeros

The Problem: When encountering a number consisting entirely of zeros (like "000"), the current implementation adds an empty string to the set after skipping all zeros. This causes two issues:

  1. The number "0" should be counted as a valid integer, not represented as an empty string
  2. Multiple sequences of all zeros would all add the same empty string, incorrectly counting them as one integer

Example: For input "a000b000c", the current code would:

  • Skip all zeros in "000" and add empty string ""
  • Skip all zeros in the second "000" and add empty string "" again
  • Return 1 (only one empty string in set), but the correct answer should still be 1 for the integer "0"

Solution: Check if we've processed any digits at all. If we skipped zeros but reached a non-digit or end of string, we should add "0" to represent the zero integer:

def numDifferentIntegers(self, word: str) -> int:
    unique_integers = set()
    index = 0
    string_length = len(word)
  
    while index < string_length:
        if word[index].isdigit():
            # Remember if we started with zeros
            had_leading_zeros = (word[index] == '0')
          
            # Skip leading zeros
            while index < string_length and word[index] == '0':
                index += 1
          
            # Mark the start of the actual number
            start_index = index
          
            # Find the end of the current number
            while index < string_length and word[index].isdigit():
                index += 1
          
            # Handle the case where the number was all zeros
            if start_index == index and had_leading_zeros:
                unique_integers.add("0")
            else:
                unique_integers.add(word[start_index:index])
        else:
            index += 1
  
    return len(unique_integers)

Pitfall 2: Edge Case with Mixed Zero Sequences

The Problem: Consider the string "00a00b0c1". The current implementation might not clearly distinguish between:

  • Pure zero sequences (should be treated as "0")
  • Numbers with leading zeros but non-zero digits following (like "001" → "1")

Example: For "00a001":

  • First "00" should be counted as "0"
  • Second "001" should be counted as "1"
  • Result should be 2 different integers

Solution: The fix in Pitfall 1 actually addresses this by explicitly checking if we had leading zeros AND whether any non-zero digits followed.

Pitfall 3: Memory Optimization Consideration

The Problem: For very long sequences of the same digit pattern, storing full strings in the set could consume unnecessary memory. For instance, a string like "1111...1111" (with millions of 1s) would store the entire sequence.

Example: Input with 10^6 consecutive ones would store a string of length 10^6 in memory.

Solution: While the current approach is correct, for production systems with memory constraints, consider:

  1. Using a hash of very long numbers instead of storing them directly
  2. Implementing a streaming comparison for extremely long integers
  3. Setting a reasonable maximum length limit based on problem constraints
def numDifferentIntegers(self, word: str) -> int:
    unique_integers = set()
    MAX_LENGTH = 1000  # Reasonable limit based on constraints
    index = 0
    string_length = len(word)
  
    while index < string_length:
        if word[index].isdigit():
            had_leading_zeros = (word[index] == '0')
          
            while index < string_length and word[index] == '0':
                index += 1
          
            start_index = index
          
            while index < string_length and word[index].isdigit():
                index += 1
          
            if start_index == index and had_leading_zeros:
                unique_integers.add("0")
            else:
                # Truncate if too long (for memory optimization)
                num_str = word[start_index:min(index, start_index + MAX_LENGTH)]
                if index - start_index > MAX_LENGTH:
                    # Add a hash for very long numbers to maintain uniqueness
                    num_str = num_str + f"_hash_{hash(word[start_index:index])}"
                unique_integers.add(num_str)
        else:
            index += 1
  
    return len(unique_integers)
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Is the following code DFS or BFS?

void search(Node root) {
  if (!root) return;
  visit(root);
  root.visited = true;
  for (Node node in root.adjacent) {
    if (!node.visited) {
      search(node);
    }
  }
}

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More