1805. Number of Different Integers in a String
Problem Description
You are given a string word
containing digits and lowercase English letters. Your task is to:
- Replace every non-digit character with a space
- 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.
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:
- It automatically handles duplicates for us
- 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:
-
Initialize: Create an empty set
s
to store unique integer strings, and set pointeri = 0
to traverse the string. -
Main traversal loop: While
i < n
(wheren
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
- Check if the current character
-
Process integer sequences: When we encounter a digit:
-
Skip leading zeros: Use a while loop to advance
i
past all consecutive'0'
characterswhile i < n and word[i] == '0': i += 1
-
Find the end of the integer: Set
j = i
and advancej
to find where the digit sequence endsj = 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
-
-
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 EvaluatorExample 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 ati = 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 ati = 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]
takesO(j-i)
time, but since we process each character once across all slicing operations, the total time for all slicing operations isO(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 storeO(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:
- The number "0" should be counted as a valid integer, not represented as an empty string
- 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:
- Using a hash of very long numbers instead of storing them directly
- Implementing a streaming comparison for extremely long integers
- 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)
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
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!