800. Similar RGB Color 🔒
Problem Description
This problem asks you to find the closest color that can be represented in shorthand format to a given color.
In RGB color notation, colors are represented as #RRGGBB
where RR, GG, and BB are two-digit hexadecimal values (00 to FF) representing red, green, and blue components respectively.
A shorthand format exists where if both digits of each component are the same, the color can be written with just one digit per component. For example:
#AABBCC
can be written as#ABC
in shorthand (where AA becomes A, BB becomes B, CC becomes C)#1155cc
can be shortened to#15c
The similarity between two colors is calculated using the formula:
-(AB - UV)² - (CD - WX)² - (EF - YZ)²
Where #ABCDEF
and #UVWXYZ
are the two colors being compared, and AB, CD, EF, UV, WX, YZ represent the decimal values of the respective hexadecimal pairs.
Given a color string in the format #ABCDEF
, you need to find and return the color that:
- Can be represented in shorthand format (meaning each component must have identical digits like 00, 11, 22, ..., FF)
- Has the highest similarity score (least negative value) with the input color
The solution works by finding the nearest "shorthand-compatible" value for each color component independently. For each two-digit hex component, it determines which valid shorthand value (00, 11, 22, ..., FF) is closest. This is done by dividing by 17 (since valid values are multiples of 17 in decimal: 0x00=0, 0x11=17, 0x22=34, etc.) and rounding to the nearest integer.
Intuition
The key insight is that colors that can be written in shorthand have a special property: their hexadecimal components must have identical digits (like 00, 11, 22, ..., FF). In decimal, these values are 0, 17, 34, 51, ..., 255, which are all multiples of 17.
Since the similarity formula uses squared differences, minimizing the total distance is equivalent to minimizing the distance for each color component independently. This is because the formula is additive: -(R₁-R₂)² - (G₁-G₂)² - (B₁-B₂)²
. Each component contributes independently to the total, so we can optimize each one separately.
For any given color component value (0-255), we need to find the nearest multiple of 17. We can do this by:
- Dividing the value by 17 to see how many "steps" of 17 it contains
- Checking if we should round up or down based on the remainder
The clever part of the solution is recognizing that when we divide by 17 and get a remainder, if that remainder is greater than 8 (which is half of 17), we should round up to the next multiple of 17. Otherwise, we round down.
For example:
- If we have value 128:
128 ÷ 17 = 7
with remainder9
. Since9 > 8
, we round up to8 × 17 = 136
(which is0x88
in hex) - If we have value 125:
125 ÷ 17 = 7
with remainder6
. Since6 ≤ 8
, we round down to7 × 17 = 119
(which is0x77
in hex)
This greedy approach of finding the nearest valid value for each component independently gives us the globally optimal solution because the components don't interact in the similarity formula.
Learn more about Math patterns.
Solution Approach
The solution implements a helper function f(x)
that converts a two-digit hexadecimal string to its nearest shorthand-compatible hexadecimal value.
Here's how the implementation works:
-
Helper Function
f(x)
:- Takes a two-digit hex string as input (like "AB")
- Converts it to decimal using
int(x, 16)
- Uses
divmod
to divide by 17, getting quotienty
and remainderz
- If remainder
z > 8
, incrementsy
by 1 (rounding up to next multiple of 17) - Multiplies
y
by 17 to get the final value - Formats it back to a two-digit hex string using
'{:02x}'.format(17 * y)
-
Main Logic:
- Extracts the three color components from the input string:
a = color[1:3]
(red component)b = color[3:5]
(green component)c = color[5:7]
(blue component)
- Applies the helper function
f
to each component independently - Combines the results using an f-string:
f'#{f(a)}{f(b)}{f(c)}'
- Extracts the three color components from the input string:
The algorithm leverages the mathematical property that valid shorthand values in decimal are 0, 17, 34, 51, ..., 238, 255
, which correspond to hex values 00, 11, 22, ..., ee, ff
.
For any input value n
:
- We calculate
n ÷ 17
to find which "bucket" it falls into - If the remainder is more than half of 17 (i.e., > 8), we round up
- Otherwise, we keep the current bucket
This greedy approach ensures each component is mapped to its nearest valid shorthand value, which maximizes the similarity score since the formula uses negative squared differences - smaller differences lead to larger (less negative) similarity values.
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 input color #09f166
.
Step 1: Extract the color components
- Red component:
09
(hex) = 9 (decimal) - Green component:
f1
(hex) = 241 (decimal) - Blue component:
66
(hex) = 102 (decimal)
Step 2: Find the nearest shorthand value for each component
For the red component (9):
- Divide by 17:
9 ÷ 17 = 0
with remainder9
- Since remainder
9 > 8
, we round up:0 + 1 = 1
- Multiply by 17:
1 × 17 = 17
- Convert to hex:
17
(decimal) =11
(hex)
For the green component (241):
- Divide by 17:
241 ÷ 17 = 14
with remainder3
- Since remainder
3 ≤ 8
, we keep:14
- Multiply by 17:
14 × 17 = 238
- Convert to hex:
238
(decimal) =ee
(hex)
For the blue component (102):
- Divide by 17:
102 ÷ 17 = 6
with remainder0
- Since remainder
0 ≤ 8
, we keep:6
- Multiply by 17:
6 × 17 = 102
- Convert to hex:
102
(decimal) =66
(hex)
Step 3: Combine the results
The final shorthand-compatible color is #11ee66
.
Let's verify this is optimal by checking the similarity score:
- Red difference:
(9 - 17)² = 64
- Green difference:
(241 - 238)² = 9
- Blue difference:
(102 - 102)² = 0
- Total similarity:
-(64 + 9 + 0) = -73
This solution minimizes each component's distance independently, ensuring the maximum possible similarity score.
Solution Implementation
1class Solution:
2 def similarRGB(self, color: str) -> str:
3 """
4 Find the closest web-safe color to the given hex color.
5 Web-safe colors have identical digits for each color component (e.g., #11, #22, #aa, #ff).
6
7 Args:
8 color: A hex color string in format "#RRGGBB"
9
10 Returns:
11 The closest web-safe color in hex format
12 """
13
14 def find_closest_shorthand(hex_component: str) -> str:
15 """
16 Find the closest shorthand hex value for a given 2-digit hex component.
17 Shorthand values are: 00, 11, 22, ..., ee, ff (multiples of 17 in decimal).
18
19 Args:
20 hex_component: A 2-character hex string (e.g., "3a")
21
22 Returns:
23 The closest shorthand hex value as a 2-character string
24 """
25 # Convert hex string to decimal
26 decimal_value = int(hex_component, 16)
27
28 # Find how many times 17 goes into the value and the remainder
29 # Since shorthand values are 0x00, 0x11, 0x22, ..., 0xff
30 # which are 0, 17, 34, ..., 255 in decimal (multiples of 17)
31 quotient, remainder = divmod(decimal_value, 17)
32
33 # If remainder is greater than 8 (half of 17), round up
34 if remainder > 8:
35 quotient += 1
36
37 # Convert back to hex format with 2 digits
38 # Multiply by 17 to get the actual shorthand value
39 return f'{17 * quotient:02x}'
40
41 # Extract RGB components from the color string
42 red_component = color[1:3]
43 green_component = color[3:5]
44 blue_component = color[5:7]
45
46 # Find the closest shorthand for each component and combine
47 return f'#{find_closest_shorthand(red_component)}{find_closest_shorthand(green_component)}{find_closest_shorthand(blue_component)}'
48
1class Solution {
2 /**
3 * Finds the most similar shorthand RGB color to the given color.
4 * A shorthand color has the format #XYZ where each component X, Y, Z
5 * is a single hex digit that represents XX, YY, ZZ respectively.
6 *
7 * @param color The input color in format #RRGGBB
8 * @return The most similar shorthand color in format #RRGGBB
9 */
10 public String similarRGB(String color) {
11 // Extract red, green, and blue components from the input color
12 String redComponent = color.substring(1, 3);
13 String greenComponent = color.substring(3, 5);
14 String blueComponent = color.substring(5, 7);
15
16 // Convert each component to its nearest shorthand value and combine
17 return "#" + findNearestShorthandValue(redComponent) +
18 findNearestShorthandValue(greenComponent) +
19 findNearestShorthandValue(blueComponent);
20 }
21
22 /**
23 * Finds the nearest shorthand hex value for a given two-digit hex component.
24 * Shorthand values are of the form AA where A is a single hex digit (00, 11, 22, ..., FF).
25 * These values are multiples of 17 in decimal (0, 17, 34, ..., 255).
26 *
27 * @param hexComponent A two-character hex string representing a color component
28 * @return The nearest shorthand hex value as a two-character string
29 */
30 private String findNearestShorthandValue(String hexComponent) {
31 // Convert hex string to decimal value
32 int decimalValue = Integer.parseInt(hexComponent, 16);
33
34 // Find the nearest multiple of 17
35 // Division by 17 gives the base multiplier
36 // If remainder > 8, round up; otherwise round down
37 int nearestMultiplier = decimalValue / 17 + (decimalValue % 17 > 8 ? 1 : 0);
38
39 // Convert back to hex format with leading zero if necessary
40 return String.format("%02x", 17 * nearestMultiplier);
41 }
42}
43
1class Solution {
2public:
3 /**
4 * Finds the most similar shorthand RGB color to the given color.
5 * A shorthand color has the format #XYZ where each component X, Y, Z
6 * is a single hex digit that represents XX, YY, ZZ respectively.
7 *
8 * @param color The input color in format #RRGGBB
9 * @return The most similar shorthand color in format #RRGGBB
10 */
11 string similarRGB(string color) {
12 // Extract red, green, and blue components from the input color
13 string red_component = color.substr(1, 2);
14 string green_component = color.substr(3, 2);
15 string blue_component = color.substr(5, 2);
16
17 // Convert each component to its nearest shorthand value and combine
18 return "#" + findNearestShorthandValue(red_component) +
19 findNearestShorthandValue(green_component) +
20 findNearestShorthandValue(blue_component);
21 }
22
23private:
24 /**
25 * Finds the nearest shorthand hex value for a given two-digit hex component.
26 * Shorthand values are of the form AA where A is a single hex digit (00, 11, 22, ..., FF).
27 * These values are multiples of 17 in decimal (0, 17, 34, ..., 255).
28 *
29 * @param hex_component A two-character hex string representing a color component
30 * @return The nearest shorthand hex value as a two-character string
31 */
32 string findNearestShorthandValue(string hex_component) {
33 // Convert hex string to decimal value
34 int decimal_value = stoi(hex_component, nullptr, 16);
35
36 // Find the nearest multiple of 17
37 // Division by 17 gives the base multiplier
38 // If remainder > 8, round up; otherwise round down
39 int nearest_multiplier = decimal_value / 17 + (decimal_value % 17 > 8 ? 1 : 0);
40
41 // Convert back to hex format with leading zero if necessary
42 char buffer[3];
43 sprintf(buffer, "%02x", 17 * nearest_multiplier);
44
45 return string(buffer);
46 }
47};
48
1/**
2 * Finds the most similar shorthand RGB color to the given color.
3 * A shorthand color has the format #XYZ where each component X, Y, Z
4 * is a single hex digit that represents XX, YY, ZZ respectively.
5 *
6 * @param color The input color in format #RRGGBB
7 * @return The most similar shorthand color in format #RRGGBB
8 */
9function similarRGB(color: string): string {
10 // Extract red, green, and blue components from the input color
11 const redComponent: string = color.substring(1, 3);
12 const greenComponent: string = color.substring(3, 5);
13 const blueComponent: string = color.substring(5, 7);
14
15 // Convert each component to its nearest shorthand value and combine
16 return "#" + findNearestShorthandValue(redComponent) +
17 findNearestShorthandValue(greenComponent) +
18 findNearestShorthandValue(blueComponent);
19}
20
21/**
22 * Finds the nearest shorthand hex value for a given two-digit hex component.
23 * Shorthand values are of the form AA where A is a single hex digit (00, 11, 22, ..., FF).
24 * These values are multiples of 17 in decimal (0, 17, 34, ..., 255).
25 *
26 * @param hexComponent A two-character hex string representing a color component
27 * @return The nearest shorthand hex value as a two-character string
28 */
29function findNearestShorthandValue(hexComponent: string): string {
30 // Convert hex string to decimal value
31 const decimalValue: number = parseInt(hexComponent, 16);
32
33 // Find the nearest multiple of 17
34 // Division by 17 gives the base multiplier
35 // If remainder > 8, round up; otherwise round down
36 const nearestMultiplier: number = Math.floor(decimalValue / 17) + (decimalValue % 17 > 8 ? 1 : 0);
37
38 // Convert back to hex format with leading zero if necessary
39 const hexValue: number = 17 * nearestMultiplier;
40 return hexValue.toString(16).padStart(2, '0');
41}
42
Time and Space Complexity
Time Complexity: O(1)
The algorithm processes a fixed-size input (a 7-character color string in the format #RRGGBB
). Breaking down the operations:
- The helper function
f(x)
is called exactly 3 times (once for each color component) - Inside
f(x)
:int(x, 16)
converts a 2-character hex string to integer -O(1)
since the string length is constant (2 characters)divmod
operation performs integer division and modulo -O(1)
- Comparison
z > 8
-O(1)
format
operation formats a single integer -O(1)
- String slicing operations (
color[1:3]
,color[3:5]
,color[5:7]
) - each isO(1)
for fixed-size slices - Final string formatting with f-string -
O(1)
since we're concatenating a fixed number of fixed-size strings
Since all operations are performed a constant number of times on fixed-size data, the overall time complexity is O(1)
.
Space Complexity: O(1)
The algorithm uses a constant amount of extra space:
- Variables
a
,b
,c
store 2-character strings -O(1)
- Inside function
f(x)
: variablesy
andz
store integers -O(1)
- Temporary strings created during formatting -
O(1)
since their size is bounded - The output string has a fixed length of 7 characters -
O(1)
The space usage doesn't scale with any input parameter, resulting in O(1)
space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Rounding Logic
A common mistake is using standard rounding functions like round()
instead of implementing the specific rounding logic needed for this problem.
Incorrect approach:
def find_closest_shorthand(hex_component: str) -> str:
decimal_value = int(hex_component, 16)
# Wrong: This doesn't guarantee multiples of 17
rounded = round(decimal_value / 17) * 17
return f'{rounded:02x}'
Why it fails: The built-in round()
function uses banker's rounding (round half to even), which may not always give the closest shorthand value for this specific problem.
Correct approach: Use divmod
and check if remainder > 8 to determine rounding direction.
2. Forgetting Hexadecimal Format Padding
When converting back to hexadecimal, forgetting to pad with zeros can cause incorrect results for small values.
Incorrect approach:
def find_closest_shorthand(hex_component: str) -> str:
decimal_value = int(hex_component, 16)
quotient, remainder = divmod(decimal_value, 17)
if remainder > 8:
quotient += 1
# Wrong: Missing zero padding
return f'{17 * quotient:x}' # Should be :02x
Why it fails: For values like 0x00 or 0x11, this would return "0" or "11" instead of "00" or "11", breaking the expected format.
3. Misunderstanding the Shorthand Format
Some might think shorthand means using single characters (like #ABC), but the problem requires returning the full 6-character format.
Incorrect approach:
def similarRGB(self, color: str) -> str:
# Wrong: Returning shorthand notation
result = ""
for i in range(1, 7, 2):
component = color[i:i+2]
decimal_value = int(component, 16)
quotient = round(decimal_value / 17)
result += f'{quotient:x}' # Single character
return f'#{result}' # Returns #abc instead of #aabbcc
Correct approach: Always return the full format like #aabbcc, where each component has identical digits.
4. Off-by-One Error in String Slicing
Incorrectly extracting color components due to wrong indices.
Incorrect approach:
# Wrong indices red_component = color[0:2] # Includes the '#' green_component = color[2:4] # Off by one blue_component = color[4:6] # Off by one
Correct approach: Remember that the '#' is at index 0, so components start at index 1.
5. Not Handling Edge Cases
Failing to consider boundary values like 0x00 and 0xff.
Test these edge cases:
#000000
should return#000000
#ffffff
should return#ffffff
#090909
should return#111111
(since 9 in decimal rounds up to 17)#080808
should return#000000
(since 8 in decimal rounds down to 0)
Which of the following uses divide and conquer strategy?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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
Want a Structured Path to Master System Design Too? Don’t Miss This!