2288. Apply Discount to Prices
Problem Description
You are given a sentence that consists of words separated by single spaces. Each word can contain digits, lowercase letters, and the dollar sign '$'
.
A word is considered a price if it:
- Starts with a dollar sign
'$'
- Is followed by only digits (no other characters)
For example:
"$100"
,"$23"
,"$6"
are valid prices"100"
(no dollar sign),"$"
(no digits),"$1e5"
(contains non-digit character 'e') are NOT valid prices
Your task is to:
- Find all words in the sentence that represent prices
- Apply a given discount percentage to each price
- Format the discounted prices to show exactly two decimal places
- Return the modified sentence with the updated prices
The input consists of:
sentence
: a string containing space-separated wordsdiscount
: an integer representing the discount percentage to apply
For each price found, you need to calculate: new_price = original_price * (1 - discount/100)
and format it with exactly two decimal places.
For example, if the sentence is "there are $1 $2 and 5$ candies in the shop"
and discount is 50
:
"$1"
becomes"$0.50"
(50% off of $1)"$2"
becomes"$1.00"
(50% off of $2)"5$"
remains unchanged (dollar sign must be at the beginning)- The result would be
"there are $0.50 $1.00 and 5$ candies in the shop"
All prices are guaranteed to have at most 10 digits.
Intuition
The problem is essentially asking us to find and replace specific patterns in a string. We need to identify words that are valid prices and transform them.
Since the sentence is already space-separated, the natural approach is to split it into individual words and process each word independently. This way, we can examine each word to determine if it's a price or not.
For each word, we need to check if it's a valid price. A valid price has two characteristics:
- It must start with
'$'
- Everything after the
'$'
must be digits only
If we find a valid price, we need to:
- Extract the numeric value (everything after the
'$'
) - Apply the discount formula:
new_price = original_price * (1 - discount/100)
- Format it back as a price string with exactly 2 decimal places
The key insight is that we can use Python's string methods to make this check simple:
w[0] == '$'
checks if the first character is a dollar signw[1:].isdigit()
checks if all remaining characters are digits- Python's f-string formatting
f'${value:.2f}'
handles the decimal place formatting automatically
After processing each word (either keeping it as-is or replacing it with the discounted price), we join all words back together with spaces to form the final sentence.
This approach is straightforward because we're working with the natural structure of the input - words separated by spaces - rather than trying to use complex pattern matching or regular expressions.
Solution Approach
The solution follows a simulation approach where we process each word in the sentence individually.
Step 1: Split the sentence into words
We use sentence.split()
to break the sentence into an array of words separated by spaces. This gives us a list where each element is a word that we can examine.
Step 2: Process each word
We iterate through each word w
in the split sentence and check if it represents a valid price:
- Check if the first character is
'$'
:w[0] == '$'
- Check if all remaining characters are digits:
w[1:].isdigit()
Both conditions must be true for a word to be considered a valid price.
Step 3: Apply discount to valid prices When we find a valid price:
- Extract the numeric value using
int(w[1:])
- this converts the string after'$'
to an integer - Calculate the discounted price:
int(w[1:]) * (1 - discount / 100)
- Format the result with exactly 2 decimal places using f-string:
f'${value:.2f}'
The complete transformation becomes: f'${int(w[1:]) * (1 - discount / 100):.2f}'
Step 4: Build the result
We collect all words (both modified prices and unchanged words) in a list ans
. Words that aren't valid prices are added unchanged, while valid prices are replaced with their discounted versions.
Step 5: Join the words back
Finally, we use ' '.join(ans)
to combine all words back into a single string with spaces between them.
Time Complexity: O(n)
where n is the length of the sentence, as we process each character once.
Space Complexity: O(n)
to store the split words and the result list.
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 a concrete example to illustrate the solution approach.
Input:
sentence = "the item costs $25 or $3 but not 15$ dollars"
discount = 20
Step 1: Split the sentence into words
words = ["the", "item", "costs", "$25", "or", "$3", "but", "not", "15$", "dollars"]
Step 2-4: Process each word and build result
Let's examine each word:
-
"the"
- First character is 't', not '$' → Not a price → Keep as "the" -
"item"
- First character is 'i', not '$' → Not a price → Keep as "item" -
"costs"
- First character is 'c', not '$' → Not a price → Keep as "costs" -
"$25"
- First character is '$' ✓, remaining "25" is all digits ✓ → Valid price!- Extract value: 25
- Apply 20% discount: 25 × (1 - 20/100) = 25 × 0.8 = 20.0
- Format: "$20.00"
-
"or"
- First character is 'o', not '$' → Not a price → Keep as "or" -
"$3"
- First character is '$' ✓, remaining "3" is all digits ✓ → Valid price!- Extract value: 3
- Apply 20% discount: 3 × (1 - 20/100) = 3 × 0.8 = 2.4
- Format: "$2.40"
-
"but"
- First character is 'b', not '$' → Not a price → Keep as "but" -
"not"
- First character is 'n', not '$' → Not a price → Keep as "not" -
"15$"
- First character is '1', not '" (Note: Dollar sign at the end doesn't count!) -
"dollars"
- First character is 'd', not '$' → Not a price → Keep as "dollars"
Step 5: Join the processed words
result = ["the", "item", "costs", "$20.00", "or", "$2.40", "but", "not", "15$", "dollars"]
Join with spaces: "the item costs $20.00 or $2.40 but not 15$ dollars"
Final Output: "the item costs $20.00 or $2.40 but not 15$ dollars"
The algorithm correctly identified only "$25"
and "$3"
as valid prices, applied the 20% discount to each, and formatted them with exactly two decimal places while leaving all other words unchanged.
Solution Implementation
1class Solution:
2 def discountPrices(self, sentence: str, discount: int) -> str:
3 """
4 Apply discount to all valid prices in a sentence.
5 A valid price starts with '$' followed by digits only.
6
7 Args:
8 sentence: Input string containing words separated by spaces
9 discount: Discount percentage to apply (0-100)
10
11 Returns:
12 Modified sentence with discounted prices formatted to 2 decimal places
13 """
14 # List to store processed words
15 result_words = []
16
17 # Process each word in the sentence
18 for word in sentence.split():
19 # Check if word is a valid price format:
20 # - Starts with '$'
21 # - Has at least one character after '$'
22 # - All characters after '$' are digits
23 if len(word) > 1 and word[0] == '$' and word[1:].isdigit():
24 # Extract the numeric value after '$'
25 original_price = int(word[1:])
26
27 # Calculate discounted price
28 discount_multiplier = 1 - discount / 100
29 discounted_price = original_price * discount_multiplier
30
31 # Format the new price with '$' prefix and 2 decimal places
32 word = f'${discounted_price:.2f}'
33
34 # Add the processed word to result
35 result_words.append(word)
36
37 # Join all words back into a sentence with spaces
38 return ' '.join(result_words)
39
1class Solution {
2 /**
3 * Applies a discount to all valid prices in a sentence.
4 * A valid price starts with '$' followed by digits only.
5 *
6 * @param sentence The input sentence containing words separated by spaces
7 * @param discount The discount percentage to apply (0-100)
8 * @return The sentence with discounted prices formatted to 2 decimal places
9 */
10 public String discountPrices(String sentence, int discount) {
11 // Split the sentence into individual words
12 String[] words = sentence.split(" ");
13
14 // Process each word in the sentence
15 for (int i = 0; i < words.length; i++) {
16 // Check if the current word is a valid price format
17 if (isValidPrice(words[i])) {
18 // Extract the numeric value (remove the '$' prefix)
19 long priceValue = Long.parseLong(words[i].substring(1));
20
21 // Calculate the discounted price
22 // Convert discount from percentage to decimal (e.g., 50 -> 0.5)
23 double discountedPrice = priceValue * (1 - discount / 100.0);
24
25 // Format the discounted price with '$' prefix and 2 decimal places
26 words[i] = String.format("$%.2f", discountedPrice);
27 }
28 }
29
30 // Join all words back into a sentence with space delimiter
31 return String.join(" ", words);
32 }
33
34 /**
35 * Validates if a string represents a valid price format.
36 * Valid format: starts with '$' followed by one or more digits.
37 *
38 * @param word The string to validate
39 * @return true if the string is a valid price format, false otherwise
40 */
41 private boolean isValidPrice(String word) {
42 // Check if the string starts with '$' and has more than one character
43 if (word.charAt(0) != '$' || word.length() == 1) {
44 return false;
45 }
46
47 // Verify all characters after '$' are digits
48 for (int i = 1; i < word.length(); i++) {
49 if (!Character.isDigit(word.charAt(i))) {
50 return false;
51 }
52 }
53
54 return true;
55 }
56}
57
1class Solution {
2public:
3 string discountPrices(string sentence, int discount) {
4 // Create input string stream to parse words from sentence
5 istringstream inputStream(sentence);
6 string word;
7 string result;
8
9 // Lambda function to validate if a word is a valid price format
10 // Valid price: starts with '$' followed by one or more digits only
11 auto isValidPrice = [](const string& str) {
12 // Check if string starts with '$' and has at least one digit after it
13 if (str[0] != '$' || str.size() == 1) {
14 return false;
15 }
16
17 // Verify all characters after '$' are digits
18 for (int i = 1; i < str.size(); ++i) {
19 if (!isdigit(str[i])) {
20 return false;
21 }
22 }
23 return true;
24 };
25
26 // Process each word in the sentence
27 while (inputStream >> word) {
28 if (isValidPrice(word)) {
29 // Extract numeric value after '$' and apply discount
30 // Convert to cents to avoid floating point precision issues
31 long long priceInCents = stoll(word.substr(1)) * (100 - discount);
32
33 // Format the discounted price with exactly 2 decimal places
34 char formattedPrice[20];
35 sprintf(formattedPrice, "$%lld.%02lld",
36 priceInCents / 100, // Dollar part
37 priceInCents % 100); // Cents part
38
39 result += formattedPrice;
40 } else {
41 // Keep non-price words unchanged
42 result += word;
43 }
44
45 // Add space separator after each word
46 result += ' ';
47 }
48
49 // Remove the trailing space from the final result
50 result.pop_back();
51
52 return result;
53 }
54};
55
1/**
2 * Applies a discount to all valid price tags in a sentence
3 * @param sentence - The input sentence containing words and price tags
4 * @param discount - The discount percentage to apply (0-100)
5 * @returns The sentence with discounted prices formatted to 2 decimal places
6 */
7function discountPrices(sentence: string, discount: number): string {
8 // Calculate the multiplier after applying the discount
9 const discountMultiplier: number = (100 - discount) / 100;
10
11 // Regular expression to match valid price format: $X where X is a positive number
12 // Pattern breakdown:
13 // ^ - Start of string
14 // (\$) - Capture group 1: Dollar sign
15 // (([1-9]\d*\.?\d*)|(0\.\d*)) - Capture group 2: Valid price number
16 // - [1-9]\d*\.?\d* - Number starting with 1-9, optional decimal part
17 // - 0\.\d* - Number starting with 0 only if followed by decimal
18 // $ - End of string
19 const pricePattern: RegExp = /^(\$)(([1-9]\d*\.?\d*)|(0\.\d*))$/;
20
21 // Process each word in the sentence
22 const processedWords: string[] = sentence.split(' ').map((word: string) => {
23 // Check if the word matches the price pattern
24 if (!pricePattern.test(word)) {
25 return word; // Return unchanged if not a valid price
26 }
27
28 // Reset regex lastIndex for next test (important for global flag)
29 pricePattern.lastIndex = 0;
30
31 // Replace the price with discounted value
32 return word.replace(pricePattern, (match: string, dollarSign: string, priceValue: string) => {
33 // Calculate discounted price and format to 2 decimal places
34 const discountedPrice: number = discountMultiplier * parseFloat(priceValue);
35 return `$${discountedPrice.toFixed(2)}`;
36 });
37 });
38
39 // Join the processed words back into a sentence
40 return processedWords.join(' ');
41}
42
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the string sentence
. This is because:
- The
split()
operation traverses the entire string once:O(n)
- The loop iterates through each word, and in total, all words combined have length proportional to
n
- Inside the loop, checking
w[0] == '$'
isO(1)
, andw[1:].isdigit()
isO(k)
wherek
is the length of the word - String formatting and arithmetic operations are
O(k)
for each word - The
join()
operation at the end isO(n)
- Since the sum of all word lengths is bounded by
n
, the overall time complexity isO(n)
The space complexity is O(n)
, where n
is the length of the string sentence
. This is because:
- The
split()
operation creates a list of words that collectively take upO(n)
space - The
ans
list stores modified words, which also takesO(n)
space in total - The final joined string returned by
join()
takesO(n)
space - Therefore, the overall space complexity is
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Price Validation Logic
A critical pitfall is incorrectly validating what constitutes a valid price. Many developers make these mistakes:
Pitfall Example:
# WRONG: Forgetting to check if there are digits after '$' if word[0] == '$' and word[1:].isdigit(): # This crashes when word is just "$" (IndexError)
Another Common Mistake:
# WRONG: Not checking for empty string after '$' if word.startswith('$') and word[1:].isdigit(): # This returns True for "$" because "".isdigit() returns False, # but we never checked if word[1:] is empty
Solution: Always verify the word has sufficient length before accessing indices:
if len(word) > 1 and word[0] == '$' and word[1:].isdigit():
# Correct: ensures word has at least 2 characters
2. Integer Overflow with Large Prices
Pitfall Example:
# Potential issue with very large numbers
original_price = int(word[1:]) # Could be up to 10 digits
While Python handles large integers well, in other languages or when converting this logic, you might encounter overflow issues. Additionally, intermediate calculations could lose precision.
Solution: Use float conversion directly for better precision handling:
original_price = float(word[1:])
discounted_price = original_price * (1 - discount / 100)
3. Floating Point Precision Errors
Pitfall Example:
# May produce unexpected results due to floating point arithmetic
discounted_price = int(word[1:]) * (1 - discount / 100)
# For $100 with 33% discount, might get 66.99999999999999 instead of 67.00
Solution:
The formatting with .2f
handles this correctly, but be aware that intermediate calculations might have precision issues. The current solution handles this appropriately with the f-string formatting.
4. Edge Cases with Single Character Words
Pitfall Example:
# Forgetting to handle single '$' character if word[0] == '$' and word[1:].isdigit(): # IndexError if word is just "$"
Solution: Always check length first:
if len(word) > 1 and word[0] == '$' and word[1:].isdigit():
5. Incorrect String Splitting or Joining
Pitfall Example:
# WRONG: Using wrong delimiter words = sentence.split(',') # Should be split by spaces, not commas # WRONG: Joining without spaces return ''.join(result_words) # Words will be concatenated without spaces
Solution: Use the correct delimiters:
words = sentence.split() # Splits by any whitespace return ' '.join(result_words) # Joins with single space
Complete Robust Solution:
class Solution:
def discountPrices(self, sentence: str, discount: int) -> str:
result_words = []
for word in sentence.split():
# Comprehensive validation:
# 1. Length check prevents IndexError
# 2. First character must be '$'
# 3. Must have digits after '$'
# 4. All characters after '$' must be digits
if (len(word) > 1 and
word[0] == '$' and
word[1:].isdigit()):
# Safe conversion and calculation
original_price = float(word[1:])
discounted_price = original_price * (1 - discount / 100)
# Proper formatting ensures exactly 2 decimal places
word = f'${discounted_price:.2f}'
result_words.append(word)
return ' '.join(result_words)
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
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!