Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1495. Friendly Movies Streamed Last Month - Leetcode Solution

Problem Description

The LeetCode problem Friendly Movies Streamed Last Month presents the following scenario: You are given a list of movies, each with a unique movieId and a list of genres that describe its content. You are also given a list of streaming sessions, where each session records a userId, a movieId, and the date the movie was streamed. The goal is to determine, for the last month, which movies were streamed by at least two users who are "friends." Friendship information is given as a list of user pairs (userId1, userId2), indicating that the two users are friends.

The key constraints are:

  • Only consider streaming sessions from the last month (i.e., within the last 30 days from today).
  • A movie is considered "friendly streamed" if at least two friends both streamed it in the last month (they do not need to stream it together).
  • Return a list of movieIds that meet the above criteria.
  • Each friendship is bidirectional (if A is a friend of B, B is a friend of A).
  • Do not count the same user twice for a movie.

Thought Process

To solve this problem, we need to efficiently determine which movies were streamed by at least two friends in the last month. The naive approach would be to check every pair of friends for every movie, but this would be computationally expensive, especially with a large number of users, movies, and sessions.

Instead, we should:

  • Filter the streaming sessions to only include those from the last month.
  • For each movie, build a set of users who streamed it in the last month.
  • For each friendship pair, check if both users are in the set of users who streamed the same movie.
  • If so, record that movie as "friendly streamed."
This approach reduces redundant checks and leverages set operations for efficiency.

Solution Approach

Let's break down the solution step-by-step:

  1. Filter Streaming Sessions:
    • Iterate through all streaming sessions and only keep those where the date is within the last 30 days.
  2. Build Movie-to-User Map:
    • For each movie, maintain a set of users who streamed it in the last month.
    • This can be represented as a dictionary: movieId -> set of userIds.
  3. Check Friendships:
    • For each friendship pair, check for every movie if both users in the pair are present in the set of users who streamed that movie.
    • If so, add the movie to the result set.
  4. Return Results:
    • Return the list of movieIds that were "friendly streamed."

We use sets for efficient lookups (O(1) time), and dictionaries to map movies to users. This avoids unnecessary nested loops and redundant checks.

Example Walkthrough

Sample Input:

  • Streaming Sessions:
    • User 1 streamed Movie 101 on 2024-05-10
    • User 2 streamed Movie 101 on 2024-05-15
    • User 3 streamed Movie 101 on 2024-05-20
    • User 1 streamed Movie 102 on 2024-05-05
    • User 2 streamed Movie 102 on 2024-04-01
  • Friendships:
    • (1, 2)
    • (2, 3)
  • Assume today's date is 2024-05-30.

Step-by-step:

  • Filter sessions to last 30 days: Only sessions from 2024-04-30 onwards remain.
  • Movie 101: Users {1,2,3} streamed it.
  • Movie 102: Only User 1 streamed it in the last month.
  • For Movie 101:
    • Friend pair (1,2): Both in the set {1,2,3} ⇒ Movie 101 qualifies.
    • Friend pair (2,3): Both in the set {1,2,3} ⇒ Movie 101 qualifies.
  • For Movie 102:
    • Friend pair (1,2): Only User 1 streamed it (User 2's session was too early) ⇒ Does not qualify.
Result: [101]

Time and Space Complexity

Brute-force Approach:

  • For each movie, for each pair of users, check if they are friends and both streamed the movie. This is O(M * U^2), where M is the number of movies and U is the number of users.
Optimized Approach:
  • Filtering sessions: O(S), where S is the number of sessions.
  • Building the movie-to-user map: O(S).
  • For each friendship (F), and for each movie (M), check if both users are in the set: O(F * M), but since lookups are O(1), this is efficient for reasonable F and M.
  • Total: O(S + F * M) time and O(M * U) space for the movie-user map.

This optimization is possible because we leverage hash sets for constant-time lookups, and avoid unnecessary pairwise comparisons.

Summary

In summary, the problem asks us to find movies streamed by at least two friends in the last month. By filtering sessions, mapping movies to users, and efficiently checking friend pairs, we avoid brute-force inefficiency. The solution relies on hash-based data structures for fast lookups and clear logic, making it both elegant and scalable.

Code Implementation

from datetime import datetime, timedelta
from collections import defaultdict

def friendly_movies_streamed_last_month(sessions, friendships, today_str):
    # sessions: List of dicts: {userId, movieId, date}
    # friendships: List of tuples: (userId1, userId2)
    # today_str: string, e.g. '2024-05-30'
    today = datetime.strptime(today_str, '%Y-%m-%d')
    last_month = today - timedelta(days=30)
    
    # Step 1: Filter sessions to last month
    filtered_sessions = [
        s for s in sessions
        if datetime.strptime(s['date'], '%Y-%m-%d') >= last_month
    ]
    
    # Step 2: movieId -> set of userIds
    movie_to_users = defaultdict(set)
    for s in filtered_sessions:
        movie_to_users[s['movieId']].add(s['userId'])
    
    # Step 3: For each friendship, check for each movie if both streamed
    result = set()
    for movie, users in movie_to_users.items():
        for u1, u2 in friendships:
            if u1 in users and u2 in users:
                result.add(movie)
                break  # Only need one friend pair per movie
    
    return list(result)

# Example usage:
# sessions = [
#     {'userId': 1, 'movieId': 101, 'date': '2024-05-10'},
#     {'userId': 2, 'movieId': 101, 'date': '2024-05-15'},
#     {'userId': 3, 'movieId': 101, 'date': '2024-05-20'},
#     {'userId': 1, 'movieId': 102, 'date': '2024-05-05'},
#     {'userId': 2, 'movieId': 102, 'date': '2024-04-01'},
# ]
# friendships = [(1,2),(2,3)]
# print(friendly_movies_streamed_last_month(sessions, friendships, '2024-05-30'))
      
#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <ctime>

using namespace std;

struct Session {
    int userId;
    int movieId;
    string date; // "YYYY-MM-DD"
};

bool is_within_last_month(const string& session_date, const string& today_str) {
    struct tm tm_today = {};
    strptime(today_str.c_str(), "%Y-%m-%d", &tm_today);
    time_t today = mktime(&tm_today);

    struct tm tm_session = {};
    strptime(session_date.c_str(), "%Y-%m-%d", &tm_session);
    time_t session_time = mktime(&tm_session);

    double diff = difftime(today, session_time);
    return diff <= 30*24*3600 && diff >= 0;
}

vector<int> friendlyMoviesStreamedLastMonth(
    const vector<Session>& sessions,
    const vector<pair<int,int>>& friendships,
    const string& today_str
) {
    unordered_map<int, unordered_set<int>> movie_to_users;
    // Step 1: Filter sessions and build map
    for (const auto& s : sessions) {
        if (is_within_last_month(s.date, today_str)) {
            movie_to_users[s.movieId].insert(s.userId);
        }
    }
    // Step 2: For each friendship, check for each movie
    vector<int> result;
    for (const auto& kv : movie_to_users) {
        int movie = kv.first;
        const auto& users = kv.second;
        for (const auto& f : friendships) {
            if (users.count(f.first) && users.count(f.second)) {
                result.push_back(movie);
                break;
            }
        }
    }
    return result;
}
      
import java.util.*;
import java.time.*;

class Session {
    int userId;
    int movieId;
    String date; // "YYYY-MM-DD"
    public Session(int u, int m, String d) {
        userId = u; movieId = m; date = d;
    }
}

public class Solution {
    public List<Integer> friendlyMoviesStreamedLastMonth(
        List<Session> sessions,
        List<int[]> friendships,
        String todayStr
    ) {
        LocalDate today = LocalDate.parse(todayStr);
        LocalDate lastMonth = today.minusDays(30);

        // Step 1: Filter sessions and build map
        Map<Integer, Set<Integer>> movieToUsers = new HashMap<>();
        for (Session s : sessions) {
            LocalDate sessionDate = LocalDate.parse(s.date);
            if (!sessionDate.isBefore(lastMonth)) {
                movieToUsers.computeIfAbsent(s.movieId, k -> new HashSet<>()).add(s.userId);
            }
        }

        // Step 2: For each friendship, check for each movie
        List<Integer> result = new ArrayList<>();
        for (Map.Entry<Integer, Set<Integer>> entry : movieToUsers.entrySet()) {
            int movie = entry.getKey();
            Set<Integer> users = entry.getValue();
            for (int[] f : friendships) {
                if (users.contains(f[0]) && users.contains(f[1])) {
                    result.add(movie);
                    break;
                }
            }
        }
        return result;
    }
}
      
function friendlyMoviesStreamedLastMonth(sessions, friendships, todayStr) {
    // sessions: Array of {userId, movieId, date}
    // friendships: Array of [userId1, userId2]
    // todayStr: 'YYYY-MM-DD'
    const today = new Date(todayStr);
    const lastMonth = new Date(today);
    lastMonth.setDate(today.getDate() - 30);

    // Step 1: Filter sessions to last month
    const movieToUsers = {};
    sessions.forEach(s => {
        const sessionDate = new Date(s.date);
        if (sessionDate >= lastMonth && sessionDate <= today) {
            if (!movieToUsers[s.movieId]) movieToUsers[s.movieId] = new Set();
            movieToUsers[s.movieId].add(s.userId);
        }
    });

    // Step 2: For each friendship, check for each movie
    const result = [];
    for (const movie in movieToUsers) {
        const users = movieToUsers[movie];
        for (const [u1, u2] of friendships) {
            if (users.has(u1) && users.has(u2)) {
                result.push(Number(movie));
                break;
            }
        }
    }
    return result;
}

// Example usage:
// const sessions = [
//   {userId: 1, movieId: 101, date: '2024-05-10'},
//   {userId: 2, movieId: 101, date: '2024-05-15'},
//   {userId: 3, movieId: 101, date: '2024-05-20'},
//   {userId: 1, movieId: 102, date: '2024-05-05'},
//   {userId: 2, movieId: 102, date: '2024-04-01'},
// ];
// const friendships = [[1,2],[2,3]];
// console.log(friendlyMoviesStreamedLastMonth(sessions, friendships, '2024-05-30'));