Start learning free

LESSON 220 OF 414 · BRUTE FORCE

Longest Subarray Without Repeating in Scala: O(n^3) Brute Force

Practice O(n3) Read the full explanation

Given an input array of integers, find the length of the longest subarray without repeating integers.

Example

Input: nums = [2, 5, 6, 2, 3, 1, 5, 6]
Output: 5
Explanation: [5, 6, 2, 3, 1] or [6, 2, 3, 1, 5] are both valid and of maximum length 5

Note:

For this lesson, your algorithm should run in O(n^3) time and use O(1) extra space.
(There exist faster solutions which we will discuss in future lessons)


STEP 1 OF 6
    Ctrl + Enter to check

    SCALA · BRUTE FORCE

    Longest Subarray Without Reapeating in Scala, explained

    Problem overview

    The method longestSubarrayWithoutReapeating receives an Array[Int] named nums and returns the length of the longest subarray in which no value appears twice. A subarray is a run of consecutive elements. Unlike a subsequence, it can't skip elements. (The method name keeps the spelling "Reapeating" from the lesson; use it exactly as written.)

    For Array(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 distinct values, and no run of six elements has all different values. The statement allows O(n^3) time, a signal that the brute force is expected.

    Approach: try every start and every end

    A subarray is fixed by its first index i and its last index j. The brute force tries every i, extends j to the right one element at a time, and checks whether the new element already occurs in the part of the subarray before it.

    Two observations keep this simple and fast enough:

    • Only the new element nums(j) can create a repeat, because the shorter subarray nums[i..j) was already valid when it was accepted. So each step has to check one value against the earlier part, not the whole subarray against itself.
    • Once nums(j) repeats a value inside the subarray, every longer subarray starting at i contains the same repeat. The search for this i can stop right there.

    The cost: there are about n^2 / 2 pairs (i, j), and checking each one scans up to n elements, hence O(n^3). A later lesson replaces the scan with a set and then removes the restart for each i.

    Step by step

    1. The answer so far. Declare var result = 0. It holds the longest valid length found. An empty array correctly gives 0, and any real subarray replaces it.
    2. Every first element. Loop with for (i <- nums.indices).
    3. Extend the end. Scala has no break in the language, so the inner loop is a while controlled by a flag. Declare var j = i and var stop = false, and loop while j < nums.length && !stop. Setting stop to true ends the loop at the next check, which is what a break would do.
    4. Check the new element. Read nums(j) into val searchValue and test nums.slice(i, j).contains(searchValue). slice(i, j) is the previous subarray, from i up to but not including j, so it doesn't contain nums(j) itself. If the value is there, set stop to true.
    5. Otherwise accept it. In the else branch the subarray nums[i..j] has no repeats, and its length is j - i + 1. Update result with math.max(result, j - i + 1), then add 1 to j. A while loop doesn't move j for you, so the increment has to be written. In the duplicate case no increment is needed because the loop is about to end.
    6. Return. After the loops, result is the last expression and the method's value.

    The solution in Scala

    class Solution {
        def longestSubarrayWithoutReapeating(nums: Array[Int]): Int = {
            var result = 0
            for (i <- nums.indices) {
                var j = i
                var stop = false
                while (j < nums.length && !stop) {
                    val searchValue = nums(j)
                    if (nums.slice(i, j).contains(searchValue)) {
                        stop = true
                    } else {
                        result = math.max(result, j - i + 1)
                        j += 1
                    }
                }
            }
            result
        }
    }
    

    Scala details:

    • slice(from, until). It returns a new array with the elements from index from up to but not including until. nums.slice(3, 3) is empty, which is why the first element of each start, where j == i, is always accepted.
    • Slices are copies. Each slice call builds a new array of up to n elements, then contains scans it. That is acceptable at this size and matches the O(n^3) budget; the temporary arrays are discarded right away.
    • var inside the loop. j and stop are declared inside the for body, so every i starts with fresh values. result lives outside so it survives all iterations.
    • math.max. It takes two numbers and returns the larger one. It is available without an import.
    • No return. The bare result is the value of the method body.

    Complexity

    Time: O(n^3). There are up to n starting indices, up to n end positions for each, and each end position does a slice and a contains of up to n elements. In practice the stop flag cuts each start short as soon as a repeat appears, but an input with all different values never triggers it, so the worst case is the full n^3.

    Space. Besides the integers, each slice creates a temporary array of up to n elements, which is thrown away on the next step. Strictly, that makes the memory in use at one moment O(n), not O(1); the lesson accepts it for simplicity.

    Edge cases and common mistakes

    • Empty array. The outer loop doesn't run, and the result stays 0.
    • All the same. Array(7, 7, 7) returns 1.
    • All different. Array(1, 2, 3) returns 3.
    • Forgetting j += 1. With a while loop, the loop never ends, because j stays the same and the flag never changes.
    • Slicing up to j + 1. That includes nums(j) in the part being searched, so every element appears to repeat itself.
    • Not stopping at a repeat. Without stop, the loop would continue past the duplicate, and later positions could count a subarray that contains the repeat.
    • Subarray versus subsequence. Elements must be next to each other. 2, 5, 6, 3, 1 skips the second 2, so it is not a subarray of the example.

    Testing

    • Array(2, 5, 6, 2, 3, 1, 5, 6) gives 5.
    • Array(7, 7, 7) gives 1.
    • Array(1, 2, 3) gives 3.
    • Array() gives 0.
    Keep going with ScalaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free