2264. Largest 3-Same-Digit Number in String
Problem Description
You are given a string num
that represents a large integer. Your task is to find the maximum "good" integer within this string.
A "good" integer must satisfy two conditions:
- It must be a substring of
num
with exactly length 3 - It must consist of only one unique digit (all three digits are the same)
For example, "777", "000", or "333" would be good integers since they have length 3 and all digits are identical. However, "123" or "001" would not be good integers because they contain different digits.
The goal is to return the maximum good integer as a string. If no such good integer exists in num
, return an empty string ""
.
Key points to note:
- A substring is a contiguous sequence of characters, meaning the three digits must appear consecutively in the original string
- Leading zeroes are allowed, so "000" is a valid good integer
- You need to find the maximum among all possible good integers (e.g., if both "222" and "777" exist in
num
, you should return "777")
The solution approach iterates from the largest possible good integer "999" down to "000", checking if each exists as a substring in num
. The first match found is returned since we're checking from largest to smallest, ensuring we get the maximum good integer.
Intuition
Since we're looking for the maximum "good" integer, and a good integer must consist of three identical digits, there are only 10 possible good integers: "000", "111", "222", ..., "999".
The key insight is that among these 10 possibilities, "999" is the largest, followed by "888", "777", and so on, down to "000". Rather than scanning through the entire string num
to find all good integers and then comparing them, we can be smarter about our search.
We can start by checking if "999" exists as a substring in num
. If it does, we immediately know it's the maximum good integer since no other good integer can be larger. If "999" doesn't exist, we check "888", then "777", and continue downward.
This greedy approach works because:
- The total number of possible good integers is small (only 10)
- We can easily generate each good integer by repeating a single digit three times:
str(i) * 3
- By checking from largest to smallest, the first match we find is guaranteed to be the maximum
The beauty of this solution is its simplicity - instead of finding all good integers and then determining the maximum, we directly search for the maximum by checking possibilities in descending order. This turns what could be a more complex string parsing problem into a straightforward enumeration of 10 cases.
Solution Approach
The implementation follows a straightforward enumeration strategy:
-
Iterate from 9 down to 0: We use a for loop with
range(9, -1, -1)
to iterate through digits from 9 to 0 in descending order. This ensures we check for larger good integers first. -
Generate each possible good integer: For each digit
i
, we create the corresponding good integer by repeating the digit three times:str(i) * 3
. This gives us strings like "999", "888", "777", etc. -
Check substring existence: We use Python's
in
operator to check if the generated three-digit string exists as a substring innum
. The expressions in num
returnsTrue
ifs
is found anywhere withinnum
. -
Return immediately upon finding: As soon as we find a good integer that exists in
num
, we return it. Since we're checking from largest to smallest, this guarantees we return the maximum good integer. -
Handle no match case: If the loop completes without finding any good integer (none of "999" through "000" exist in
num
), we return an empty string""
.
The code uses a compact Python feature - the walrus operator :=
- which allows us to assign the generated string to variable s
while simultaneously using it in the condition check. This makes the code more concise:
if (s := str(i) * 3) in num:
return s
The time complexity is O(n) where n is the length of num
, since in the worst case we might check all 10 possible good integers, and each substring check takes O(n) time. The space complexity is O(1) as we only use a constant amount of extra space.
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 num = "6777133339"
.
We want to find the maximum "good" integer (three identical consecutive digits) in this string.
Step 1: Check for "999"
- Generate:
str(9) * 3 = "999"
- Check: Is "999" in "6777133339"? No
- Continue to next digit
Step 2: Check for "888"
- Generate:
str(8) * 3 = "888"
- Check: Is "888" in "6777133339"? No
- Continue to next digit
Step 3: Check for "777"
- Generate:
str(7) * 3 = "777"
- Check: Is "777" in "6777133339"?
- Looking at the string: "6777133339" - Yes! "777" exists at position 1-3
- Since we found a match, immediately return "777"
We don't need to check further (for "666", "555", etc.) because we're searching from largest to smallest. The first match we find is guaranteed to be the maximum good integer.
Note that "333" also exists in the string at positions 5-7, but since we found "777" first (which is larger), we correctly return "777" as the maximum good integer.
Another Example: If num = "222244400"
, the algorithm would:
- Check "999" → Not found
- Check "888" → Not found
- Check "777" → Not found
- Check "666" → Not found
- Check "555" → Not found
- Check "444" → Found at positions 4-6! Return "444"
Even though "222" and "000" also exist, we return "444" since it's the largest good integer present.
Solution Implementation
1class Solution:
2 def largestGoodInteger(self, num: str) -> str:
3 # Iterate through digits from 9 to 0 in descending order
4 # This ensures we find the largest "good integer" first
5 for digit in range(9, -1, -1):
6 # Create a three-digit string with the same digit (e.g., "999", "888", etc.)
7 three_same_digits = str(digit) * 3
8
9 # Check if this three-digit pattern exists as a substring in num
10 if three_same_digits in num:
11 # Return the first (largest) match found
12 return three_same_digits
13
14 # No three consecutive same digits found
15 return ""
16
1class Solution {
2 /**
3 * Finds the largest "good integer" substring in the given number string.
4 * A "good integer" consists of exactly three consecutive identical digits.
5 *
6 * @param num the input number string to search within
7 * @return the largest good integer found, or empty string if none exists
8 */
9 public String largestGoodInteger(String num) {
10 // Iterate from largest possible digit (9) to smallest (0)
11 for (int digit = 9; digit >= 0; digit--) {
12 // Create a string of three consecutive identical digits
13 String targetPattern = String.valueOf(digit).repeat(3);
14
15 // Check if the input string contains this pattern
16 if (num.contains(targetPattern)) {
17 // Return the first (largest) pattern found
18 return targetPattern;
19 }
20 }
21
22 // No good integer found, return empty string
23 return "";
24 }
25}
26
1class Solution {
2public:
3 string largestGoodInteger(string num) {
4 // Iterate from '9' down to '0' to find the largest digit first
5 for (char digit = '9'; digit >= '0'; --digit) {
6 // Create a string of three consecutive identical digits
7 string targetPattern(3, digit);
8
9 // Check if this pattern exists in the input string
10 if (num.find(targetPattern) != string::npos) {
11 // Return the first (largest) pattern found
12 return targetPattern;
13 }
14 }
15
16 // No pattern of three consecutive identical digits found
17 return "";
18 }
19};
20
1/**
2 * Finds the largest "good integer" substring of length 3 with all identical digits
3 * @param num - The input string to search within
4 * @returns The largest good integer as a string, or empty string if none found
5 */
6function largestGoodInteger(num: string): string {
7 // Iterate from largest digit (9) to smallest (0) to find the largest good integer first
8 for (let digit = 9; digit >= 0; digit--) {
9 // Create a substring of three repeated digits (e.g., "999", "888", etc.)
10 const targetPattern = String(digit).repeat(3);
11
12 // Check if the current pattern exists in the input string
13 if (num.includes(targetPattern)) {
14 // Return immediately when found (guaranteed to be the largest due to iteration order)
15 return targetPattern;
16 }
17 }
18
19 // No good integer found, return empty string
20 return '';
21}
22
Time and Space Complexity
Time Complexity: O(10 × n)
or simplified to O(n)
, where n
is the length of the string num
.
The algorithm iterates through 10 possible digits (from 9 down to 0). For each digit i
, it creates a string pattern str(i) * 3
(which takes O(1)
time) and checks if this pattern exists in the input string using the in
operator. The substring search operation s in num
takes O(n)
time in the worst case, where n
is the length of num
. Since we perform this search at most 10 times (a constant), the overall time complexity is O(10 × n) = O(n)
.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space. The variable s
stores a string of length 3 (like "999", "888", etc.), which requires O(1)
space. The loop variable i
also takes constant space. No additional data structures that scale with the input size are used, resulting in constant space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Attempting to Parse and Compare as Integers
A common mistake is trying to convert substrings to integers for comparison, which fails when dealing with "000":
Incorrect approach:
def largestGoodInteger(self, num: str) -> str:
max_good = -1
for i in range(len(num) - 2):
substring = num[i:i+3]
if substring[0] == substring[1] == substring[2]:
max_good = max(max_good, int(substring)) # Problem: int("000") = 0
return str(max_good) if max_good != -1 else ""
This fails because int("000")
equals 0
, losing the original format. When comparing, "000" would incorrectly be considered smaller than other single-digit good integers.
Correct approach: Compare strings directly or iterate from largest to smallest as shown in the solution.
2. Incorrect String Comparison Logic
Another pitfall is assuming string comparison works the same as numeric comparison without considering lexicographic ordering:
Incorrect approach:
def largestGoodInteger(self, num: str) -> str:
max_good = ""
for i in range(len(num) - 2):
substring = num[i:i+3]
if substring[0] == substring[1] == substring[2]:
max_good = max(max_good, substring) # Works but less efficient
return max_good
While this actually works correctly (since "999" > "888" > ... > "000" in lexicographic order), it's less efficient as it requires scanning the entire string and performing multiple comparisons.
3. Off-by-One Errors in Sliding Window
When manually implementing a sliding window approach, it's easy to make boundary errors:
Incorrect approach:
def largestGoodInteger(self, num: str) -> str:
for i in range(len(num)): # Should be len(num) - 2
if i + 2 < len(num): # Extra boundary check needed
substring = num[i:i+3]
# ... rest of logic
Better approach: Use range(len(num) - 2)
to avoid index out of bounds errors naturally.
4. Not Handling Edge Cases
Forgetting to handle cases where the string length is less than 3:
Solution: The provided approach naturally handles this since the in
operator will simply return False
for strings shorter than the pattern being searched.
What's the output of running the following function using the following tree as input?
1def serialize(root):
2 res = []
3 def dfs(root):
4 if not root:
5 res.append('x')
6 return
7 res.append(root.val)
8 dfs(root.left)
9 dfs(root.right)
10 dfs(root)
11 return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4 StringJoiner res = new StringJoiner(" ");
5 serializeDFS(root, res);
6 return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10 if (root == null) {
11 result.add("x");
12 return;
13 }
14 result.add(Integer.toString(root.val));
15 serializeDFS(root.left, result);
16 serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2 let res = [];
3 serialize_dfs(root, res);
4 return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8 if (!root) {
9 res.push("x");
10 return;
11 }
12 res.push(root.val);
13 serialize_dfs(root.left, res);
14 serialize_dfs(root.right, res);
15}
16
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!