Facebook Pixel

2424. Longest Uploaded Prefix

MediumUnion FindDesignBinary Indexed TreeSegment TreeHash TableBinary SearchOrdered SetHeap (Priority Queue)
Leetcode Link

Problem Description

You need to design a data structure that tracks video uploads to a server. The videos are numbered from 1 to n, and they can be uploaded in any order.

The key concept is the longest uploaded prefix: this is the maximum consecutive sequence of videos starting from video 1 that have all been uploaded. For example:

  • If videos 1, 2, and 3 have been uploaded, the longest uploaded prefix is 3
  • If videos 1, 2, 4, and 5 have been uploaded (but not 3), the longest uploaded prefix is only 2

You need to implement a class LUPrefix with three methods:

  1. LUPrefix(int n): Initialize the data structure for n videos
  2. upload(int video): Mark the specified video as uploaded to the server
  3. longest(): Return the current length of the longest uploaded prefix

The solution uses a variable r to track the current longest prefix and a set s to store which videos have been uploaded. When a video is uploaded, it's added to the set, then the code checks if consecutive videos starting from r + 1 are present in the set, updating r accordingly.

For example:

  • Initially, no videos are uploaded, so longest() returns 0
  • Upload video 2: longest() still returns 0 (video 1 is missing)
  • Upload video 1: longest() returns 2 (videos 1 and 2 form a prefix)
  • Upload video 4: longest() still returns 2 (video 3 is missing)
  • Upload video 3: longest() returns 4 (videos 1, 2, 3, 4 form a prefix)
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The main insight is that we only need to track one critical boundary: where the continuous sequence of uploaded videos breaks. Think of it like filling gaps in a number line starting from 1.

When we upload a video, two scenarios can happen:

  1. The video creates or extends a continuous sequence from 1
  2. The video is uploaded "out of order" and will be useful later

The key observation is that the longest prefix can only grow when we upload a video that's exactly one position after our current longest prefix (r + 1). When this happens, we might have already uploaded videos r + 2, r + 3, etc., so we need to keep checking how far we can extend.

Why use a set? We need to quickly check if a specific video number has been uploaded, and sets give us O(1) lookup time. We could use a boolean array of size n, but a set is more memory-efficient when uploads are sparse.

The clever part is maintaining the variable r as a "checkpoint". Instead of recalculating the longest prefix from scratch each time (which would require checking videos 1, 2, 3... every time), we remember where we left off. When a new video is uploaded, we only need to check if we can advance from our current position. This transforms what could be an O(n) operation per upload into an amortized O(1) operation, since each video position is checked at most once across all uploads.

The while loop in the upload method captures this perfectly: keep advancing r as long as the next video in sequence has been uploaded. Once we hit a gap, we stop and wait for that missing video to be uploaded later.

Learn more about Union Find, Segment Tree, Binary Search and Heap (Priority Queue) patterns.

Solution Approach

The solution uses a simulation approach with two key data structures:

  • A variable r to track the current longest uploaded prefix
  • A set s to store all uploaded video numbers

Initialization (__init__):

  • Set r = 0 since no videos have been uploaded yet
  • Initialize an empty set s to track uploaded videos
  • The parameter n isn't stored since we only need to track actual uploads

Upload Operation (upload):

  1. Add the video number to the set: self.s.add(video)
  2. Check if this upload can extend the longest prefix:
    while self.r + 1 in self.s:
        self.r += 1
    This loop continues as long as the next consecutive video (r + 1) exists in our set, incrementing r each time.

Longest Query (longest):

  • Simply return the current value of r

Why This Works: The algorithm maintains an invariant: r always represents the longest prefix where all videos from 1 to r have been uploaded. When we upload a new video:

  • If it's not r + 1, it doesn't immediately affect the longest prefix
  • If it is r + 1, we can extend the prefix and need to check if we can extend further

Time Complexity:

  • upload: Amortized O(1) - Although the while loop might iterate multiple times, each video position is visited at most once across all upload operations
  • longest: O(1) - Direct return of stored value

Space Complexity: O(min(n, m)) where m is the number of uploads made, as we only store uploaded videos in the set.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a scenario with n = 5 videos to understand how the algorithm works:

Initial State:

  • r = 0 (no videos form a prefix yet)
  • s = {} (empty set)

Step 1: Upload video 3

  • Add 3 to set: s = {3}
  • Check if r + 1 (which is 1) is in set: No
  • r remains 0
  • longest() returns 0

Step 2: Upload video 2

  • Add 2 to set: s = {2, 3}
  • Check if r + 1 (which is 1) is in set: No
  • r remains 0
  • longest() returns 0

Step 3: Upload video 1

  • Add 1 to set: s = {1, 2, 3}
  • Check if r + 1 (which is 1) is in set: Yes!
    • Increment r to 1
    • Check if r + 1 (which is 2) is in set: Yes!
    • Increment r to 2
    • Check if r + 1 (which is 3) is in set: Yes!
    • Increment r to 3
    • Check if r + 1 (which is 4) is in set: No, stop
  • r is now 3
  • longest() returns 3

Step 4: Upload video 5

  • Add 5 to set: s = {1, 2, 3, 5}
  • Check if r + 1 (which is 4) is in set: No
  • r remains 3
  • longest() returns 3

Step 5: Upload video 4

  • Add 4 to set: s = {1, 2, 3, 4, 5}
  • Check if r + 1 (which is 4) is in set: Yes!
    • Increment r to 4
    • Check if r + 1 (which is 5) is in set: Yes!
    • Increment r to 5
    • Check if r + 1 (which is 6) is in set: No, stop
  • r is now 5
  • longest() returns 5

The key insight: When we uploaded video 1, we immediately extended the prefix from 0 to 3 because videos 2 and 3 were already waiting in the set. Similarly, when we uploaded video 4, we extended from 3 to 5 because video 5 was already uploaded. The algorithm efficiently "catches up" whenever a gap is filled.

Solution Implementation

1class LUPrefix:
2    def __init__(self, n: int):
3        """
4        Initialize the data structure with n videos (numbered 1 to n).
5      
6        Args:
7            n: The total number of videos
8        """
9        # Track the longest consecutive prefix starting from video 1
10        self.longest_prefix = 0
11        # Set to store uploaded video numbers
12        self.uploaded_videos = set()
13
14    def upload(self, video: int) -> None:
15        """
16        Upload a video to the server.
17      
18        Args:
19            video: The video number being uploaded
20        """
21        # Add the video to the set of uploaded videos
22        self.uploaded_videos.add(video)
23      
24        # Extend the longest prefix if possible
25        # Check if the next consecutive video after current prefix is available
26        while self.longest_prefix + 1 in self.uploaded_videos:
27            self.longest_prefix += 1
28
29    def longest(self) -> int:
30        """
31        Return the length of the longest uploaded prefix.
32        A prefix of length k means videos 1 through k have all been uploaded.
33      
34        Returns:
35            The length of the longest consecutive uploaded prefix
36        """
37        return self.longest_prefix
38
39
40# Your LUPrefix object will be instantiated and called as such:
41# obj = LUPrefix(n)
42# obj.upload(video)
43# param_2 = obj.longest()
44
1class LUPrefix {
2    // Tracks the length of the longest consecutive prefix starting from 1
3    private int longestPrefixLength;
4  
5    // Set to store all uploaded video IDs
6    private Set<Integer> uploadedVideos;
7
8    /**
9     * Initializes the data structure with n total videos (1 to n).
10     * @param n The total number of videos
11     */
12    public LUPrefix(int n) {
13        this.longestPrefixLength = 0;
14        this.uploadedVideos = new HashSet<>();
15    }
16
17    /**
18     * Uploads a video with the given ID and updates the longest prefix.
19     * @param video The ID of the video being uploaded
20     */
21    public void upload(int video) {
22        // Add the video to the set of uploaded videos
23        uploadedVideos.add(video);
24      
25        // Extend the longest prefix as far as possible
26        // Check if the next consecutive video after current prefix is available
27        while (uploadedVideos.contains(longestPrefixLength + 1)) {
28            longestPrefixLength++;
29        }
30    }
31
32    /**
33     * Returns the length of the longest uploaded prefix.
34     * A prefix of length k means videos 1 through k have all been uploaded.
35     * @return The length of the longest consecutive prefix starting from 1
36     */
37    public int longest() {
38        return longestPrefixLength;
39    }
40}
41
42/**
43 * Your LUPrefix object will be instantiated and called as such:
44 * LUPrefix obj = new LUPrefix(n);
45 * obj.upload(video);
46 * int param_2 = obj.longest();
47 */
48
1class LUPrefix {
2public:
3    /**
4     * Constructor: Initialize the data structure with n videos (numbered 1 to n)
5     * @param n: The total number of videos
6     */
7    LUPrefix(int n) {
8        // Note: n is provided but not used in this implementation
9        // The data structure tracks uploaded videos dynamically
10    }
11  
12    /**
13     * Upload a video to the server
14     * After uploading, update the longest uploaded prefix if possible
15     * @param video: The ID of the video being uploaded
16     */
17    void upload(int video) {
18        // Add the video to the set of uploaded videos
19        uploadedVideos.insert(video);
20      
21        // Extend the longest prefix as far as possible
22        // Check if consecutive videos starting from (longestPrefix + 1) are uploaded
23        while (uploadedVideos.count(longestPrefix + 1)) {
24            ++longestPrefix;
25        }
26    }
27  
28    /**
29     * Get the length of the longest uploaded prefix
30     * A prefix of length k means videos 1, 2, ..., k are all uploaded
31     * @return: The length of the longest uploaded prefix
32     */
33    int longest() {
34        return longestPrefix;
35    }
36  
37private:
38    // The length of the longest uploaded prefix (initially 0)
39    int longestPrefix = 0;
40  
41    // Set to store all uploaded video IDs
42    unordered_set<int> uploadedVideos;
43};
44
45/**
46 * Your LUPrefix object will be instantiated and called as such:
47 * LUPrefix* obj = new LUPrefix(n);
48 * obj->upload(video);
49 * int param_2 = obj->longest();
50 */
51
1// The length of the longest uploaded prefix (initially 0)
2let longestPrefix: number = 0;
3
4// Set to store all uploaded video IDs
5let uploadedVideos: Set<number> = new Set();
6
7/**
8 * Initialize the data structure with n videos (numbered 1 to n)
9 * @param n - The total number of videos
10 */
11function LUPrefix(n: number): void {
12    // Reset the data structure for a new instance
13    longestPrefix = 0;
14    uploadedVideos = new Set<number>();
15    // Note: n is provided but not used in this implementation
16    // The data structure tracks uploaded videos dynamically
17}
18
19/**
20 * Upload a video to the server
21 * After uploading, update the longest uploaded prefix if possible
22 * @param video - The ID of the video being uploaded
23 */
24function upload(video: number): void {
25    // Add the video to the set of uploaded videos
26    uploadedVideos.add(video);
27  
28    // Extend the longest prefix as far as possible
29    // Check if consecutive videos starting from (longestPrefix + 1) are uploaded
30    while (uploadedVideos.has(longestPrefix + 1)) {
31        longestPrefix++;
32    }
33}
34
35/**
36 * Get the length of the longest uploaded prefix
37 * A prefix of length k means videos 1, 2, ..., k are all uploaded
38 * @returns The length of the longest uploaded prefix
39 */
40function longest(): number {
41    return longestPrefix;
42}
43

Time and Space Complexity

Time Complexity:

  • __init__(n): O(1) - Simply initializes two variables
  • upload(video): O(n) in the worst case - Adding to the set is O(1), but the while loop can iterate up to n times when we upload the last video that completes a consecutive sequence from 1 to n
  • longest(): O(1) - Simply returns the stored value

Overall time complexity for n upload operations: O(n) amortized. While a single upload can take O(n) time, across all uploads, each video from 1 to n can only cause the while loop to increment r once, giving us a total of O(n) operations across all uploads.

Space Complexity: O(n) - The set s stores uploaded video numbers, which in the worst case can contain all n videos. The variable r uses O(1) space.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Assumption About Upload Order

A common mistake is assuming that the upload method needs to handle videos being uploaded multiple times or that uploading the same video twice should have special behavior. The problem statement guarantees each video is uploaded only once, but developers might add unnecessary checks:

Incorrect approach:

def upload(self, video: int) -> None:
    if video in self.uploaded_videos:  # Unnecessary check
        return
    self.uploaded_videos.add(video)
    # ... rest of the logic

Solution: Trust the problem constraints and avoid redundant checks that add complexity without value.

2. Inefficient Prefix Extension Logic

Some developers might try to recalculate the entire longest prefix from scratch after each upload:

Inefficient approach:

def upload(self, video: int) -> None:
    self.uploaded_videos.add(video)
    # Recalculating from 1 every time - O(n) worst case per upload
    self.longest_prefix = 0
    while self.longest_prefix + 1 in self.uploaded_videos:
        self.longest_prefix += 1

Solution: Maintain the invariant that longest_prefix always represents the current longest valid prefix. Only check for extension starting from the current position, not from the beginning.

3. Using Array Instead of Set

Using an array or list to track uploaded videos leads to inefficient lookups:

Poor choice:

def __init__(self, n: int):
    self.longest_prefix = 0
    self.uploaded_videos = []  # O(n) lookup time

def upload(self, video: int) -> None:
    self.uploaded_videos.append(video)
    while self.longest_prefix + 1 in self.uploaded_videos:  # O(n) per check
        self.longest_prefix += 1

Solution: Use a set for O(1) average-case lookup time, which is crucial for the efficiency of the while loop in the upload method.

4. Storing Unnecessary Information

Some solutions might store the parameter n or create a boolean array of size n:

Memory-inefficient approach:

def __init__(self, n: int):
    self.n = n  # Not needed
    self.uploaded = [False] * (n + 1)  # Wastes O(n) space
    self.longest_prefix = 0

Solution: Only store what's necessary. The set of uploaded videos and the current longest prefix are sufficient. The total number n isn't needed for the logic.

5. Off-by-One Errors with Video Numbering

Videos are numbered from 1 to n, not 0 to n-1. A common mistake is mixing up the indexing:

Incorrect:

def upload(self, video: int) -> None:
    self.uploaded_videos.add(video)
    while self.longest_prefix in self.uploaded_videos:  # Should be longest_prefix + 1
        self.longest_prefix += 1

Solution: Remember that videos start at 1, and we're always checking if the next video (longest_prefix + 1) exists to extend our prefix.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following is a min heap?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More