Problem overview
The method longestSubarray receives an Array[Int] of positive integers, nums, and a number S. It must find the longest continuous subarray whose sum is at most S, and return its start and end indices as a two-element array.
For nums = [3, 2, 5, 2, 2, 1, 1, 3, 1, 2] and S = 11, the answer is [3, 8]. The elements at indices 3 to 8 are 2, 2, 1, 1, 3, 1, which add up to 10. That is six elements. Adding one more element on either side, a 5 on the left or a 2 on the right, would push the sum over 11.
The statement asks for O(n^2) time and O(1) extra space, which is exactly what two nested loops give.
Approach: try every start, extend the end
Every subarray is fixed by its start i and its end j. The outer loop picks the start. The inner loop moves the end to the right one element at a time, and the sum grows by one addition at each step instead of being recomputed.
After each addition, two things decide whether the subarray becomes the new best:
- its sum must be at most
S, and - it must be longer than the longest valid subarray found so far.
The number of elements from i to j inclusive is j - i + 1. The comparison uses a strict "longer than", so among subarrays of equal length the first one found, the one with the smallest start, is kept.
Because the numbers are positive, the inner loop could stop as soon as the sum passes S: adding more positive numbers can only make it bigger. The lesson's version doesn't stop early, and the condition sum <= S handles those cases anyway. Stopping early is an optimization, and it doesn't change the worst-case bound.
Step by step
- Create
answeras avarholding an emptyArray[Int](). It will hold the best start and end found so far. It is replaced by a new two-element array every time a longer valid subarray turns up. - Create
maxLenas avarstarting at 0. It stores the length of the best subarray. Starting at 0 means that any valid subarray, which has at least one element, is longer. - Loop
iovernums.indices. Each index is a possible start. The expressionnums.indicesis the range of valid indices, from 0 until the length of the array. - Declare
sumas avarequal to 0 inside that loop. It holds the sum of the subarray fromitoj, and declaring it inside the outer loop resets it for each new start. - Loop
joveri until nums.size. Eachjis a possible end for the subarray that starts ati. Starting atimakes the one-element subarray the first one tried. - Add
nums(j)tosum. Nowsumis the sum of the elements fromitoj. - Check both conditions with
sum <= S && maxLen < (j - i + 1). - Update
maxLentoj - i + 1inside theif. - Update
answertoArray(i, j). A later, longer subarray overwrites it. - Return
answerafter both loops.
The solution in Scala
class Solution {
def longestSubarray(nums: Array[Int], S: Int): Array[Int] = {
var answer = Array[Int]()
var maxLen = 0
for (i <- nums.indices) {
var sum = 0
for (j <- i until nums.size) {
sum += nums(j)
if (sum <= S && maxLen < (j - i + 1)) {
maxLen = j - i + 1
answer = Array(i, j)
}
}
}
return answer
}
}
The Scala details worth knowing:
nums.indicesandnums.size. For an array,sizeandlengthare the same number.indicesis a range, sofor (i <- nums.indices)is the same loop asfor (i <- 0 until nums.length). On a two-element array,indicesprints asRange 0 until 2.Array[Int]()is an empty array. The type in square brackets is needed, since there are no elements to infer it from.- A parameter called
S. Scala parameters can start with a capital letter. The usual style is lower case, but here the name matches the problem statement. - Short-circuit
&&. The right side is only evaluated when the left side is true. Both sides here are cheap, so the order doesn't change the result. - Array literals.
Array(i, j)creates a newArray[Int]. Sinceansweris avar, it simply points at the new array.
Complexity
Time: O(n^2). For each of the n starts, the inner loop visits up to n - i ends, so the total is about n * (n + 1) / 2 steps. Each step does a constant amount of work, because the sum is extended by one element and not recomputed.
Space: O(1). The variables are single integers, and the answer array always has at most two elements.
Edge cases and common mistakes
- No valid subarray. If every element is bigger than
S, nothing satisfies the condition and the method returns the empty array. For example,[5, 6, 7]withS = 4gives[]. - The whole array fits.
[1, 2, 3]withS = 100gives[0, 2]. - Ties.
[1, 2, 1, 2]withS = 3has several subarrays of length 2 with sum 3. The strict comparison keeps the first,[0, 1]. - One element.
[4]withS = 4gives[0, 0]. The end index equals the start index, and the length is 1. - Length off by one. The length is
j - i + 1. Writingj - iwould give 0 for a single element, and it would never beatmaxLen = 0. - Declaring
sumoutside the outer loop. The sum would carry over from the previous start and include elements that aren't in the current subarray. - Using
<instead of<=forS. The statement says "at most", so a sum equal toSis allowed. - An empty array. The loops never run and the method returns an empty array.
Testing
[3, 2, 5, 2, 2, 1, 1, 3, 1, 2]withS = 11gives[3, 8].[5, 6, 7]withS = 4gives[].[1, 2, 3]withS = 100gives[0, 2].[1, 2, 1, 2]withS = 3gives[0, 1].[4]withS = 4gives[0, 0].
