Find X Value of Array I
MedPrompt
You are given an array of positive integers nums, and a positive integer k.
You are allowed to perform an operation once on nums, where in each operation you can remove any non-overlapping prefix and suffix from nums such that nums remains non-empty.
You need to find the x-value of nums, which is the number of ways to perform this operation so that the product of the remaining elements leaves a remainder of x when divided by k.
Return an array result of size k where result[x] is the x-value of nums for 0 <= x <= k - 1.
A prefix of an array is a subarray that starts from the beginning of the array and extends to any point within it.
A suffix of an array is a subarray that starts at any point within the array and extends to the end of the array.
Note that the prefix and suffix to be chosen for the operation can be empty.
Example 1:
Input: nums = [1,2,3,4,5], k = 3
Output: [9,2,4]
Explanation:
- For
x = 0, the possible operations include all possible ways to remove non-overlapping prefix/suffix that do not removenums[2] == 3. - For
x = 1, the possible operations are:- Remove the empty prefix and the suffix
[2, 3, 4, 5].numsbecomes[1]. - Remove the prefix
[1, 2, 3]and the suffix[5].numsbecomes[4].
- Remove the empty prefix and the suffix
- For
x = 2, the possible operations are:- Remove the empty prefix and the suffix
[3, 4, 5].numsbecomes[1, 2]. - Remove the prefix
[1]and the suffix[3, 4, 5].numsbecomes[2]. - Remove the prefix
[1, 2, 3]and the empty suffix.numsbecomes[4, 5]. - Remove the prefix
[1, 2, 3, 4]and the empty suffix.numsbecomes[5].
- Remove the empty prefix and the suffix
Example 2:
Input: nums = [1,2,4,8,16,32], k = 4
Output: [18,1,2,0]
Explanation:
- For
x = 0, the only operations that do not result inx = 0are:- Remove the empty prefix and the suffix
[4, 8, 16, 32].numsbecomes[1, 2]. - Remove the empty prefix and the suffix
[2, 4, 8, 16, 32].numsbecomes[1]. - Remove the prefix
[1]and the suffix[4, 8, 16, 32].numsbecomes[2].
- Remove the empty prefix and the suffix
- For
x = 1, the only possible operation is:- Remove the empty prefix and the suffix
[2, 4, 8, 16, 32].numsbecomes[1].
- Remove the empty prefix and the suffix
- For
x = 2, the possible operations are:- Remove the empty prefix and the suffix
[4, 8, 16, 32].numsbecomes[1, 2]. - Remove the prefix
[1]and the suffix[4, 8, 16, 32].numsbecomes[2].
- Remove the empty prefix and the suffix
- For
x = 3, there is no possible way to perform the operation.
Example 3:
Input: nums = [1,1,2,1,1], k = 2
Output: [9,6]
Constraints:
1 <= nums[i] <= 1091 <= nums.length <= 1051 <= k <= 5
Approaches
3 approaches with complexity analysis and trade-offs.
This approach is the most straightforward and directly follows the problem description. It involves generating every possible non-empty subarray of nums, calculating the product of its elements modulo k, and then incrementing a counter for the corresponding remainder. This method is simple to conceptualize but computationally very expensive.
Algorithm
- Initialize a result array
resof sizekto all zeros. - Iterate through all possible start indices
lfrom0ton-1. - For each
l, iterate through all possible end indicesrfromlton-1. - For each subarray
nums[l...r]:- Initialize a
productvariable to 1. - Iterate from
i = ltor, updating the product:product = (product * nums[i]) % k. - Increment the count in
resfor the calculated remainder:res[product]++.
- Initialize a
- Return
res.
Walkthrough
The core idea is to systematically check every single subarray. A subarray can be defined by its start and end indices, let's say i and j. We can use a pair of nested loops to iterate through all possible values of i and j where 0 <= i <= j < nums.length.
For each such subarray nums[i...j], we need to compute the product of its elements. A third, inner loop is used for this purpose. To prevent the product from becoming excessively large and causing overflow, we apply the modulo k operation at each step of the multiplication. After computing the final product modulo k for a subarray, we increment the corresponding counter in our result array.
class Solution { public int[] findXValue(int[] nums, int k) { int n = nums.length; int[] result = new int[k]; for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { long product = 1; // Calculate product for subarray nums[i...j] for (int l = i; l <= j; l++) { product = (product * nums[l]) % k; } result[(int)product]++; } } return result; }}Complexity
Time
O(N³), where N is the number of elements in `nums`. There are O(N²) subarrays. For each subarray, calculating the product takes, on average, O(N) time, leading to a cubic time complexity.
Space
O(k). We only need an array of size `k` to store the results. Given `k` is small, this is effectively O(1).
Trade-offs
Pros
Simple to understand and implement.
Directly translates the problem statement into code.
Cons
Extremely inefficient due to three nested loops.
Will result in a 'Time Limit Exceeded' (TLE) error for the given constraints.
This approach improves upon the naive brute-force method by eliminating redundant calculations. Instead of re-calculating the product for each subarray from scratch, we can compute it incrementally. For a fixed starting point l, as we extend the subarray to the right by incrementing r, the product of the new subarray nums[l...r] can be found by simply multiplying the product of the previous subarray nums[l...r-1] with the new element nums[r].
Algorithm
- Initialize a result array
resof sizekto all zeros. - Iterate through all possible start indices
lfrom0ton-1. - For each
l, initialize acurrent_product = 1. - Iterate through all possible end indices
rfromlton-1. - Update
current_productby multiplying withnums[r]modulok. - Increment the count in
resfor thecurrent_product. - Return
res.
Walkthrough
We use two nested loops. The outer loop fixes the starting index i of the subarrays. The inner loop iterates from i to the end of the array, defining the ending index j. Inside the inner loop, we maintain a currentProduct variable. For each j, we update currentProduct by multiplying it with nums[j] (and taking the modulo k). This currentProduct represents the product of the subarray nums[i...j]. We then use this value to update our result array. This optimization removes the third, innermost loop from the previous approach, reducing the complexity from cubic to quadratic.
class Solution { public int[] findXValue(int[] nums, int k) { int n = nums.length; int[] result = new int[k]; for (int i = 0; i < n; i++) { long currentProduct = 1; for (int j = i; j < n; j++) { currentProduct = (currentProduct * nums[j]) % k; result[(int)currentProduct]++; } } return result; }}Complexity
Time
O(N²). Two nested loops iterate through all O(N²) subarrays, and the work inside the inner loop is now O(1).
Space
O(k). We only need an array of size `k` to store the results.
Trade-offs
Pros
More efficient than the O(N³) approach.
Still relatively easy to understand and implement.
Cons
Still too slow for the given constraints (
N <= 10^5) and will result in TLE.
This is the most efficient approach, which uses dynamic programming. The key insight is that we can build up the solution by processing the array one element at a time. At each index i, we can calculate the product remainders for all subarrays ending at i by using the information we computed for subarrays ending at the previous index i-1. Since k is very small, this becomes highly efficient.
Algorithm
- Initialize a
longresult arrayresof sizekto all zeros. - Initialize a
longarrayprev_freqof sizekto all zeros. This will store remainder frequencies for subarrays ending at the previous element. - Iterate through each
numin the input arraynums. - For each
num, create a newlongarraycurrent_freqof sizek. - Let
v = num % k. - Increment
current_freq[v]by 1. This accounts for the subarray containing onlynum. - Iterate
pfrom0tok-1. For eachp, ifprev_freq[p]is greater than 0, it means there areprev_freq[p]subarrays ending at the previous element with remainderp. Extending them withnumgives a new remainder(p * v) % k. Addprev_freq[p]tocurrent_freq[(p * v) % k]. - Add the counts from
current_freqto the totalresarray. - Update
prev_freqtocurrent_freqfor the next iteration. - After the loop, cast the
longresult array to anintarray and return it.
Walkthrough
We iterate through nums, and for each element num, we maintain a frequency map (an array of size k) called currentFreq. currentFreq[x] will store the number of subarrays ending at the current element num whose product modulo k is x.
For each num, currentFreq is computed as follows:
- A new subarray consisting of just
numis formed. Its product remainder isnum % k. So, we initializecurrentFreq[num % k]to 1. - We then consider extending all subarrays that ended at the previous element. Let's say
prevFreqstored the frequencies for the previous element. For each remainderpfrom0tok-1, there wereprevFreq[p]subarrays. When we appendnumto them, they form new subarrays with a product remainder of(p * (num % k)) % k. We addprevFreq[p]to the count for this new remainder incurrentFreq.
After computing currentFreq for the current num, we add its counts to our global result array. Then, currentFreq becomes prevFreq for the next iteration.
Note: The total number of subarrays can be up to 10^5 * (10^5 + 1) / 2, which is about 5 * 10^9. This means the counts can exceed the capacity of a 32-bit integer. Therefore, we must use long for our frequency and result arrays to avoid overflow.
class Solution { public int[] findXValue(int[] nums, int k) { // Use long for counts to prevent overflow, as total subarrays can be large. long[] result = new long[k]; // prevFreq[rem] stores the count of subarrays ending at the previous element // with a product remainder of 'rem'. long[] prevFreq = new long[k]; for (int num : nums) { long[] currentFreq = new long[k]; int val = num % k; // Case 1: The subarray consists of only the current element `num`. currentFreq[val]++; // Case 2: Extend all subarrays ending at the previous element. for (int p = 0; p < k; p++) { if (prevFreq[p] > 0) { int newRem = (p * val) % k; currentFreq[newRem] += prevFreq[p]; } } // Add the counts from subarrays ending at the current element to the total result. for (int x = 0; x < k; x++) { result[x] += currentFreq[x]; } // The current frequencies become the previous frequencies for the next iteration. prevFreq = currentFreq; } // Cast the long results to int as per typical problem constraints. int[] finalResult = new int[k]; for (int i = 0; i < k; i++) { finalResult[i] = (int) result[i]; } return finalResult; }}Complexity
Time
O(N * k). We iterate through the N elements of `nums`. For each element, we perform a loop of size `k` to update frequencies. Since `k` is a small constant, the complexity is effectively linear in N.
Space
O(k). We use a few arrays of size `k` (`result`, `prev_freq`, `current_freq`). Since `k` is very small (`k <= 5`), this is effectively constant space.
Trade-offs
Pros
Highly efficient and optimal for the given constraints.
Passes within the time limits.
Cons
The logic is more complex than the brute-force approaches.
Requires careful handling of potential integer overflows for the counts, as the total number of subarrays can be very large. Using
longfor counts is necessary.
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.