Maximize Subarrays After Removing One Conflicting Pair
HardPrompt
You are given an integer n which represents an array nums containing the numbers from 1 to n in order. Additionally, you are given a 2D array conflictingPairs, where conflictingPairs[i] = [a, b] indicates that a and b form a conflicting pair.
Remove exactly one element from conflictingPairs. Afterward, count the number of non-empty subarrays of nums which do not contain both a and b for any remaining conflicting pair [a, b].
Return the maximum number of subarrays possible after removing exactly one conflicting pair.
Example 1:
Input: n = 4, conflictingPairs = [[2,3],[1,4]]
Output: 9
Explanation:
- Remove
[2, 3]fromconflictingPairs. Now,conflictingPairs = [[1, 4]]. - There are 9 subarrays in
numswhere[1, 4]do not appear together. They are[1],[2],[3],[4],[1, 2],[2, 3],[3, 4],[1, 2, 3]and[2, 3, 4]. - The maximum number of subarrays we can achieve after removing one element from
conflictingPairsis 9.
Example 2:
Input: n = 5, conflictingPairs = [[1,2],[2,5],[3,5]]
Output: 12
Explanation:
- Remove
[1, 2]fromconflictingPairs. Now,conflictingPairs = [[2, 5], [3, 5]]. - There are 12 subarrays in
numswhere[2, 5]and[3, 5]do not appear together. - The maximum number of subarrays we can achieve after removing one element from
conflictingPairsis 12.
Constraints:
2 <= n <= 1051 <= conflictingPairs.length <= 2 * nconflictingPairs[i].length == 21 <= conflictingPairs[i][j] <= nconflictingPairs[i][0] != conflictingPairs[i][1]
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly simulates the problem statement. It iterates through each conflicting pair, assumes it's the one to be removed, and then calculates the total number of valid subarrays for the remaining set of conflicting pairs. The maximum count found across all these simulations is the result.
Algorithm
- Initialize
max_subarraysto 0. - Iterate through each pair
c_kinconflictingPairsto select it for removal. - In each iteration, create a temporary list of the other
m-1conflicting pairs. - For this temporary list, calculate the total number of valid subarrays:
- Create an
end_pointsarray of sizen, initialized ton. For each pair(u, v)(withu < v) in the temporary list, updateend_points[u-1] = min(end_points[u-1], v-1). - Compute a
limitarray of sizen.limit[i]represents the maximum valid end indexjfor a subarray starting ati. This is calculated by iterating backwards:limit[n-1] = end_points[n-1]andlimit[i] = min(limit[i+1], end_points[i]). - The number of valid subarrays for the current set of pairs is
sum_{i=0 to n-1} (limit[i] - i).
- Create an
- Update
max_subarrayswith the maximum count found. - Return
max_subarrays.
Walkthrough
The fundamental idea is to test every possible scenario. There are m (length of conflictingPairs) pairs, so there are m scenarios, one for each pair being removed.
For a given set of conflicting pairs, we can count the number of valid subarrays. A subarray nums[i...j] is valid if for every conflicting pair (a, b) in the set, the subarray does not contain both a and b. Let's assume u = min(a,b) and v = max(a,b). The condition for a conflict is that the subarray starting at index i and ending at j must contain both u (at index u-1) and v (at index v-1), which means i <= u-1 and j >= v-1.
To count valid subarrays, we can iterate through each possible starting index i from 0 to n-1. For each i, we need to find the maximum possible ending index j such that nums[i...j] is valid. This means for all remaining conflicting pairs (u, v), it's not the case that i <= u-1 and j >= v-1. This is equivalent to j < v-1 for all pairs (u,v) where u-1 >= i. Therefore, for a fixed i, j must be less than limit[i] = min({v-1 | (u,v) is a conflict, u-1 >= i}). The number of valid subarrays starting at i is limit[i] - i.
The limit array can be computed in O(n+m) time. We first build an end_points array where end_points[k] stores the minimum v-1 for all pairs starting at k. Then, limit[i] can be found with a backward pass: limit[i] = min(limit[i+1], end_points[i]).
Since we repeat this O(n+m) calculation for each of the m pairs to be removed, the total complexity is O(m * (n+m)).
import java.util.ArrayList;import java.util.Arrays;import java.util.List; class Solution { public int maximizeSubarrays(int n, int[][] conflictingPairs) { long maxSubarrays = 0; int m = conflictingPairs.length; if (m == 0) { return (int)((long)n * (n + 1) / 2); } for (int i = 0; i < m; i++) { // Step 2a: Create a list of pairs excluding the i-th pair List<int[]> currentPairs = new ArrayList<>(); for (int j = 0; j < m; j++) { if (i == j) continue; currentPairs.add(conflictingPairs[j]); } // Step 2b: Compute limit array int[] endPoints = new int[n]; Arrays.fill(endPoints, n); for (int[] pair : currentPairs) { int u = Math.min(pair[0], pair[1]); int v = Math.max(pair[0], pair[1]); endPoints[u - 1] = Math.min(endPoints[u - 1], v - 1); } int[] limit = new int[n]; if (n > 0) { limit[n - 1] = endPoints[n - 1]; for (int j = n - 2; j >= 0; j--) { limit[j] = Math.min(limit[j + 1], endPoints[j]); } } // Step 2c: Calculate current count long currentCount = 0; for (int j = 0; j < n; j++) { currentCount += Math.max(0, limit[j] - j); } // Step 2d: Update max maxSubarrays = Math.max(maxSubarrays, currentCount); } return (int)maxSubarrays; }}Complexity
Time
O(m * (n + m)). The outer loop runs `m` times. Inside the loop, creating the list of pairs takes `O(m)`, computing `end_points` takes `O(m)`, computing `limit` takes `O(n)`, and summing the counts takes `O(n)`. Thus, each iteration is `O(n+m)`. Since `m` can be up to `2n`, this is effectively O(n^2).
Space
O(n + m), where `n` is the number of elements and `m` is the number of conflicting pairs. This is for storing the temporary list of pairs and the `end_points` and `limit` arrays.
Trade-offs
Pros
Conceptually straightforward and follows the problem statement directly.
Easier to implement correctly compared to more optimized solutions.
Cons
The time complexity of O(m * (n+m)) is too slow for the given constraints and will likely result in a 'Time Limit Exceeded' error.
This optimized approach avoids redundant calculations by pre-computing necessary information. It first calculates a baseline count of valid subarrays assuming no pairs are removed. Then, for each pair, it calculates the 'gain' in valid subarrays if that specific pair were to be removed. The gain is the number of subarrays that were previously invalid only because of this one pair. By using clever pre-computation, including prefix sums and maps, the gain for each pair can be found very quickly, leading to an efficient overall solution.
Algorithm
- Pre-computation:
- Normalize all pairs
(a,b)to(u,v)whereu < v. Group pairs by their starting pointuand sort them byv. - For each starting index
i, findep1[i]andep2[i], the smallest and second-smallestv-1from pairs starting ati+1. - Compute
min1[i]andmin2[i]arrays.min1[i]is the smallestv-1among all pairs(u,v)withu-1 >= i, andmin2[i]is the second smallest. These are computed in a single backward pass overep1andep2. - Calculate the
base_countof valid subarrays with all pairs present:sum_{i=0 to n-1} (min1[i] - i). - For efficiency, group indices
iby the value ofmin1[i]. For each value group, create a prefix sum array of the potential gains(min2[i] - min1[i]).
- Normalize all pairs
- Gain Calculation:
- Initialize
max_gain = 0. - For each conflicting pair
c_k = (u_k, v_k):- The potential gain is non-zero only for indices
i <= u_k-1wheremin1[i] == v_k-1. - Use the pre-computed map and prefix sums to find the total gain for this
c_kinO(log n)time via binary search. - Update
max_gain = max(max_gain, current_gain).
- The potential gain is non-zero only for indices
- Initialize
- Result:
- The final answer is
base_count + max_gain.
- The final answer is
Walkthrough
Instead of re-computing from scratch, we can express the answer as a base value plus an improvement. Let C be the full set of conflicting pairs and c_k be the pair we remove.
The number of valid subarrays for C - {c_k} is count(C) + gain(c_k).
count(C) is the base number of valid subarrays. A subarray nums[i...j] is valid for C if j < min1[i], where min1[i] is the minimum v-1 over all pairs (u,v) in C with u-1 >= i. The base count is sum_{i=0 to n-1} (min1[i] - i).
gain(c_k) is the number of subarrays that become valid after removing c_k. These are the subarrays nums[i...j] that contain c_k but no other pair from C. This happens if c_k was the 'tightest' constraint for some starting indices i. Let min2[i] be the second-smallest v-1 for pairs (u,v) with u-1 >= i. If we remove c_k = (u_k, v_k), the limit min1[i] becomes min2[i] if and only if v_k-1 was equal to min1[i]. This can only occur for i <= u_k-1.
The gain from removing c_k is sum_{i | i <= u_k-1 and v_k-1 == min1[i]} (min2[i] - min1[i]).
To calculate this sum efficiently for every k, we pre-compute min1 and min2 arrays. Then, we group all indices i based on the value of min1[i]. For each value Y, we have a list of indices where min1[i] == Y. We also pre-compute prefix sums of min2[i] - min1[i] for each of these lists. When calculating the gain for c_k = (u_k, v_k), we look up the list for Y = v_k-1, use binary search to find how many indices in that list are <= u_k-1, and use the prefix sum array to get the total gain in O(log n) time.
import java.util.*; class Solution { public int maximizeSubarrays(int n, int[][] conflictingPairs) { int m = conflictingPairs.length; if (m == 0) { return (int)((long)n * (n + 1) / 2); } List<List<Integer>> adj = new ArrayList<>(); for (int i = 0; i <= n; i++) adj.add(new ArrayList<>()); for (int[] p : conflictingPairs) { int u = Math.min(p[0], p[1]); int v = Math.max(p[0], p[1]); adj.get(u).add(v); } for (int i = 1; i <= n; i++) Collections.sort(adj.get(i)); int[] ep1 = new int[n], ep2 = new int[n]; Arrays.fill(ep1, n); Arrays.fill(ep2, n); for (int i = 1; i <= n; i++) { if (adj.get(i).size() > 0) ep1[i - 1] = adj.get(i).get(0) - 1; if (adj.get(i).size() > 1) ep2[i - 1] = adj.get(i).get(1) - 1; } int[] min1 = new int[n], min2 = new int[n]; min1[n - 1] = ep1[n - 1]; min2[n - 1] = ep2[n - 1]; for (int i = n - 2; i >= 0; i--) { int[] candidates = {min1[i + 1], min2[i + 1], ep1[i], ep2[i]}; Arrays.sort(candidates); min1[i] = candidates[0]; min2[i] = candidates[1]; } long baseCount = 0; for (int i = 0; i < n; i++) baseCount += Math.max(0, min1[i] - i); Map<Integer, List<Integer>> valToIndices = new HashMap<>(); Map<Integer, List<Long>> valToPrefixSums = new HashMap<>(); for (int i = 0; i < n; i++) { valToIndices.computeIfAbsent(min1[i], k -> new ArrayList<>()).add(i); } for (Map.Entry<Integer, List<Integer>> entry : valToIndices.entrySet()) { int val = entry.getKey(); List<Integer> indices = entry.getValue(); List<Long> prefixSums = new ArrayList<>(); long currentSum = 0; for (int idx : indices) { currentSum += (long)min2[idx] - min1[idx]; prefixSums.add(currentSum); } valToPrefixSums.put(val, prefixSums); } long maxGain = 0; for (int[] p : conflictingPairs) { int u = Math.min(p[0], p[1]); int v = Math.max(p[0], p[1]); int y = v - 1; if (!valToIndices.containsKey(y)) continue; List<Integer> indices = valToIndices.get(y); int searchVal = u - 1; int low = 0, high = indices.size() - 1, p = -1; while (low <= high) { int mid = low + (high - low) / 2; if (indices.get(mid) <= searchVal) { p = mid; low = mid + 1; } else { high = mid - 1; } } if (p != -1) { maxGain = Math.max(maxGain, valToPrefixSums.get(y).get(p)); } } return (int)(baseCount + maxGain); }}Complexity
Time
O(n + m log m). Sorting pairs for each start point takes `O(m log d_max)` where `d_max` is max degree, worst case `O(m log m)`. Computing `ep`, `min`, and `base_count` takes `O(n)`. Building the maps and prefix sums takes `O(n)`. The final loop runs `m` times, with each iteration taking `O(log n)` for binary search. The dominant factor is typically the initial sorting of pairs.
Space
O(n + m). The adjacency list `adj` takes `O(m)` space. The `ep`, `min1`, `min2` arrays take `O(n)`. The maps for pre-computation store a total of `n` indices and `n` prefix sum values across all keys, so they also take `O(n)` space.
Trade-offs
Pros
Highly efficient with a time complexity that passes the given constraints.
Reduces redundant work by calculating a base value and then an incremental gain.
Cons
Significantly more complex to understand and implement.
Requires careful handling of multiple data structures and pre-computation steps.
Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.