3119. Maximum Number of Potholes That Can Be Fixed ๐
Problem Description
You are given a string road
, consisting only of characters "x"
and "."
, where each "x"
denotes a pothole and each "."
denotes a smooth road, along with an integer budget
.
In one repair operation, you can repair n
consecutive potholes for a price of n + 1
.
Return the maximum number of potholes that can be fixed such that the sum of the costs of all repairs doesn't exceed the given budget.
Intuition
To solve this problem optimally, we need to utilize a counting and greedy approach. The idea is to count the number of consecutive potholes of each length and attempt to repair larger groups first since they offer a better ratio of potholes repaired to cost incurred.
- First, convert each segment of consecutive potholes into a count and store these counts in an array called
cnt
. cnt[k]
indicates the number of segments of consecutive potholes of lengthk
.- Start with the longest sequences of potholes (
k
) and see how many of these you can afford to repair based on thebudget
. - This is done by calculating
t
, which is the minimum between the number of segmentscnt[k]
and how many your budget allows (budget // (k + 1)
). Fix as many ast
segments of lengthk
. - For each segment repaired, reduce the
budget
accordingly and increase the count of repaired potholes (ans
). - If there are more segments than you can afford to fix, consider them as shorter segments for future budget calculations by updating
cnt[k-1]
. - Repeat the process for smaller segments until the budget is exhausted or all segments are considered.
By following the above logic, you maximize the number of potholes repaired within the given budget by focusing on cost-effective repairs.
Solution Approach
The solution implements a counting and greedy technique to fix potholes optimally. Here's a step-by-step walkthrough of the approach:
-
Augment the Road: Append a
"."
to theroad
string to ensure any trailing pothole segments are processed. -
Initialize Variables:
- Create an array
cnt
of sizen
wheren
is the length of the augmentedroad
. This will store the counts of consecutive pothole segments. - Use a variable
k
to keep track of the current length of a consecutive pothole segment. - Set
ans
to 0 to track the total number of potholes repaired.
- Create an array
-
Count Consecutive Potholes:
- Traverse the
road
character by character using a loop. - Increment
k
for every pothole"x"
encountered. - When a smooth road
"."
is encountered after a sequence of potholes, updatecnt[k]
by the number of such sequences and resetk
to 0.
- Traverse the
-
Repair Potholes Greedily:
- Iterate over possible lengths of pothole sequences from
n-1
to 1. - For each length
k
, determine the number of segmentst
that can be completely fixed given the currentbudget
.- This is calculated by
t = min(budget // (k + 1), cnt[k])
.
- This is calculated by
- Increment
ans
byt * k
because for each segment of lengthk
,k
potholes are repaired. - Decrease the
budget
byt * (k + 1)
, accounting for the segments repaired. - If
cnt[k] > t
, add the leftover potholes to the next smaller group of potholes by updatingcnt[k-1]
.
- Iterate over possible lengths of pothole sequences from
-
End Condition:
- Stop the process if the
budget
is exhausted.
- Stop the process if the
The algorithm prioritizes longer segments first due to their cost efficiency, ensuring the maximum number of potholes are repaired within the given budget.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a simple example to illustrate the approach. Consider the following input:
- String
road
:"..xx..xxx...x"
- Budget:
6
Step 1: Augment the Road
To ensure all potholes are considered, append a "."
to the road
. It becomes "..xx..xxx...x."
.
Step 2: Initialize Variables
Create an array, cnt
, initialized to zeros with size equal to the length of the augmented road. Initialize k = 0
to count consecutive potholes and ans = 0
to sum repaired potholes.
Step 3: Count Consecutive Potholes
Iterate over the string:
- For index
2
: encounter"x"
, incrementk
to1
. - For index
3
: encounter another"x"
, incrementk
to2
. - For index
4
: encounter"."
(end of segment), updatecnt[2]
to1
, resetk
to0
. - For index
6
: encounter"x"
, incrementk
to1
. - For index
7
: another"x"
, incrementk
to2
. - For index
8
: another"x"
, incrementk
to3
. - For index
9
: encounter"."
(end of segment), updatecnt[3]
to1
, resetk
to0
. - For index
12
: encounter"x"
, incrementk
to1
. - For index
13
: encounter"."
(end of segment), updatecnt[1]
to1
, resetk
to0
.
cnt
now looks like [0, 1, 1, 1, ..., 0]
.
Step 4: Repair Potholes Greedily
Starting from length 3 downwards:
-
For
k = 3
: Can fixt = min(budget // (3+1), cnt[3]) = min(6 // 4, 1) = 1
. Repair this segment:- Update
ans += 3 * 1 = 3
. - Adjust
budget -= 4 * 1 = 2
. - If
cnt[3] > t
, add excess tocnt[2]
. In this case, it's not needed.
- Update
-
For
k = 2
: Can fixt = min(budget // (2+1), cnt[2]) = min(2 // 3, 1) = 0
. Budget doesn't allow repair of length-2 segments. -
For
k = 1
: Can fixt = min(budget // (1+1), cnt[1]) = min(2 // 2, 1) = 1
. Repair this segment:- Update
ans += 1 * 1 = 4
. - Adjust
budget -= 2 * 1 = 0
.
- Update
Step 5: End Condition
The budget is exhausted, so stop the process.
Result
The maximum number of potholes repaired is 4
.
Solution Implementation
1class Solution:
2 def maxPotholes(self, road: str, budget: int) -> int:
3 # Add a sentinel character at the end of the road to handle the last group of potholes
4 road += "."
5
6 # Determine the length of the modified road
7 n = len(road)
8
9 # Initialize a list to keep count of the number of segments of continuous potholes of length i
10 cnt = [0] * n
11
12 # Variable to keep track of the current streak of continuous potholes
13 k = 0
14
15 # Traverse each character in the road string
16 for c in road:
17 if c == "x":
18 # Increment the streak if the character is a pothole
19 k += 1
20 elif k:
21 # Record the streak count and reset the streak
22 cnt[k] += 1
23 k = 0
24
25 # Initialize the answer to account for the maximum number of potholes that can be fixed
26 ans = 0
27
28 # Iterate over the cnt list from the end to the beginning
29 for k in range(n - 1, 0, -1):
30 if cnt[k] == 0:
31 # Continue if there are no segments of length k
32 continue
33
34 # Determine the maximum number of full groups of length (k + 1) that can be fixed
35 t = min(budget // (k + 1), cnt[k])
36
37 # Increase the answer by the number of potholes fixed in these full groups
38 ans += t * k
39
40 # Deduct the cost for fixing these full groups from the budget
41 budget -= t * (k + 1)
42
43 # Stop if there is no budget left
44 if budget == 0:
45 break
46
47 # Add the remainder of the groups that couldn't be fully fixed to the next lower count
48 cnt[k - 1] += cnt[k] - t
49
50 return ans
51
1class Solution {
2 public int maxPotholes(String road, int budget) {
3 // Append a non-'x' character to handle trailing potholes
4 road += ".";
5 int n = road.length();
6
7 // Array to keep track of contiguous pothole counts
8 int[] cnt = new int[n];
9 int contiguous = 0;
10
11 // Iterate through the road string
12 for (char ch : road.toCharArray()) {
13 if (ch == 'x') {
14 // Increase count if pothole is found
15 ++contiguous;
16 } else if (contiguous > 0) {
17 // Record contiguous potholes and reset counter
18 ++cnt[contiguous];
19 contiguous = 0;
20 }
21 }
22
23 int maxPotholesFixed = 0;
24
25 // Process from longest contiguous segment to shortest
26 for (contiguous = n - 1; contiguous > 0 && budget > 0; --contiguous) {
27 // Calculate the maximum number of contiguous segments fixable
28 int fixableSegments = Math.min(budget / (contiguous + 1), cnt[contiguous]);
29
30 // Add total potholes fixed in this step
31 maxPotholesFixed += fixableSegments * contiguous;
32
33 // Deduct budget used for fixing
34 budget -= fixableSegments * (contiguous + 1);
35
36 // Adjust count array to shift unfixable segments to the next smaller length
37 cnt[contiguous - 1] += cnt[contiguous] - fixableSegments;
38 }
39
40 return maxPotholesFixed;
41 }
42}
43
1#include <vector>
2#include <string>
3#include <algorithm>
4
5class Solution {
6public:
7 int maxPotholes(std::string road, int budget) {
8 road.push_back('.'); // Append a '.' to handle the last section of potholes easily.
9 int n = road.size();
10 std::vector<int> count(n, 0); // Count of contiguous pothole segments of different lengths.
11 int contiguousCount = 0; // Length of the current contiguous segment of 'x'.
12
13 // Traverse the road to count contiguous segments of potholes ('x').
14 for (char& c : road) {
15 if (c == 'x') {
16 ++contiguousCount; // Increment length of contiguous 'x' segment.
17 } else if (contiguousCount) {
18 ++count[contiguousCount]; // Record the count of this segment length.
19 contiguousCount = 0; // Reset for the next segment.
20 }
21 }
22
23 int maxFixedPotholes = 0; // Maximum potholes that can be fixed with the budget.
24
25 // Work backward through potential segment lengths to maximize potholes fixed.
26 for (int length = n - 1; length > 0 && budget > 0; --length) {
27 // Calculate how many segments of this length can be fixed within budget.
28 int fixable = std::min(budget / (length + 1), count[length]);
29 maxFixedPotholes += fixable * length; // Increase fixed pothole count.
30 budget -= fixable * (length + 1); // Decrease budget according to segments fixed.
31 count[length - 1] += count[length] - fixable; // Roll over any unfixed segments.
32 }
33
34 return maxFixedPotholes; // Return the total count of fixed potholes.
35 }
36};
37
1// Function to calculate the maximum number of potholes that can be fixed within a given budget.
2function maxPotholes(road: string, budget: number): number {
3 // Append a '.' to the road string to handle the edge case of potholes at the end of the road
4 road += '.';
5
6 // Length of the modified road string
7 const n: number = road.length;
8
9 // Array to count contiguous sequences of 'x' (potholes) of each length
10 const cnt: number[] = Array(n).fill(0);
11
12 let k: number = 0; // Variable to count current contiguous 'x' length
13
14 // Iterate over each character in the road string
15 for (const c of road) {
16 if (c === 'x') {
17 // Count contiguous potholes
18 ++k;
19 } else if (k > 0) {
20 // When a sequence of potholes ends, update the count array
21 ++cnt[k];
22 k = 0; // Reset count
23 }
24 }
25
26 let ans: number = 0; // Variable to store the maximum number of potholes fixed
27
28 // Iterate from largest possible contiguous pothole sequence downwards
29 for (k = n - 1; k > 0 && budget > 0; --k) {
30 // Calculate how many sequences of length 'k' can be fixed within the budget
31 const t: number = Math.min(Math.floor(budget / (k + 1)), cnt[k]);
32
33 // Increment the answer by the number of potholes fixed
34 ans += t * k;
35
36 // Reduce the budget by the cost of fixing the potholes
37 budget -= t * (k + 1);
38
39 // Add remaining sequences of current length to the next lower length
40 cnt[k - 1] += cnt[k] - t;
41 }
42
43 return ans; // Return the maximum number of potholes fixed
44}
45
Time and Space Complexity
The time complexity of the code is O(n)
, where n
is the length of the string road
. This is because the code iterates through the string road
a few times: once to count the sequence of potholes and once more to calculate the maximum number of potholes that can be filled within the given budget.
The space complexity is also O(n)
, as the auxiliary array cnt
used in the code has a size proportional to n
. This array is utilized to keep track of the lengths of contiguous pothole segments.
Learn more about how to find time and space complexity quickly.
Which of the following is a min heap?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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
Want a Structured Path to Master System Design Too? Donโt Miss This!