Problem overview
The function longest_subarray_without_reapeating receives a vector of integers, nums, and returns the length of the longest subarray in which no value appears twice. A subarray is a run of neighbouring elements, so [5, 6, 2] is a subarray of [2, 5, 6, 2, 3], while [2, 6] is not.
For nums = [2, 5, 6, 2, 3, 1, 5, 6] the answer is 5. Both [5, 6, 2, 3, 1] and [6, 2, 3, 1, 5] have five different values, and no window of six elements avoids a repeat. An empty vector gives 0.
The lesson accepts O(n^3) time and O(1) extra space. Faster solutions exist, but this lesson is about checking every candidate directly.
Approach: try every start, extend until a repeat
A subarray is fixed by where it starts and where it ends. The solution picks every start i, then grows the subarray one element at a time by moving the end j to the right.
When the subarray grows to include nums[j], only that one value is new, so the only thing to check is whether it already appears in the part before it, nums[i..j]. If it does, every longer subarray from the same start contains that repeat too, so the inner loop can stop with break. If it doesn't, the subarray from i to j is valid, and its length j - i + 1 competes with the best length found so far.
On the example, start i = 1 extends over 5, 6, 2, 3 and 1, reaching length 5. At j = 6 the value 5 is already in nums[1..6], so that start stops. Start i = 0 stops earlier: at j = 3 the second 2 repeats the first, leaving a best length of 3 from that start.
Why does the lesson accept such a slow method? It is the easiest one to get right, and it shows exactly where the time goes: the same elements are re-scanned by contains every time the end moves one step. Faster approaches avoid that repeated scanning, so it helps to see the cost here first.
Step by step
- Keep the best length so far in
result, a mutablei32that starts at 0. For an empty vector it is never updated, which is the right answer. - Loop over the starting index
iin0..nums.len(). - Loop over the ending index
jini..nums.len(). Starting atimeans the one-element subarray is checked first. - Check for a repeat. Take the slice
nums[i..j], the elements already in the subarray, and ask whether it containsnums[j]. On a repeat, leave the inner loop. - Record the length. With no repeat, the subarray
nums[i..=j]is valid and hasj - i + 1elements. Take the larger of that andresult, converting theusizelength toi32. - Return
resultas the last expression.
The solution in Rust
impl Solution {
fn longest_subarray_without_reapeating(nums: Vec<i32>) -> i32 {
let mut result: i32 = 0;
for i in 0..nums.len() {
for j in i..nums.len() {
if nums[i..j].contains(&nums[j]) {
break;
}
result = result.max((j - i + 1) as i32);
}
}
result
}
}
The Rust details worth a closer look:
- Slicing with a range.
nums[i..j]borrows the elements fromiup to but not includingj. Whenj == ithe slice is empty, so the first check at each start never finds a repeat. Slicing withi..jpanics only ifi > jorjis past the end, and neither can happen here. containstakes a reference. A slice ofi32looks for an&i32, so the call iscontains(&nums[j]). It scans the slice from the left, which is why it costs time proportional to the slice length.result.max(...).maxis a method on integers that returns the larger of two values. Both sides must have the same type, which is why the length is converted withas i32.usizesubtraction.j - i + 1is safe becausej >= iin the inner loop, so the subtraction never goes below zero.
Complexity
Time: O(n^3). There are about n^2 / 2 pairs (i, j), and for each one contains may scan up to j - i elements. When all values are different, no break ever fires and the total work grows with n^3. The break makes inputs with many repeats cheaper, but not the worst case.
Space: O(1). Slices only borrow the vector, so the function stores a few integers.
Edge cases and common mistakes
- Empty vector. Neither loop runs and the function returns 0.
- All values equal. For
[4, 4, 4]each start has a valid subarray of length 1, and the second element repeats, so the answer is 1. - All values different. For
[1, 2, 3, 4]no repeat is found, and the answer is the full length, 4. - Subarray versus subsequence. The elements must be neighbours. For
[1, 2, 1, 2, 3]the answer is 3, from[1, 2, 3]at the end, and not a count of the distinct values that skips elements. - Checking
nums[i..=j]for the repeat. That slice always containsnums[j], so every subarray would look like a repeat and the answer would be 0. The check must stop beforej. - Using
continueinstead ofbreak. After a repeat, longer subarrays from the same start are invalid too. Withcontinue, the loop could count a later subarray that still contains the repeat.
Testing
nums = [2, 5, 6, 2, 3, 1, 5, 6]gives 5.nums = []gives 0.nums = [4, 4, 4]gives 1.nums = [1, 2, 3, 4]gives 4.nums = [1, 2, 1, 2, 3]gives 3.
