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:
movieId
s that meet the above criteria.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:
Let's break down the solution step-by-step:
date
is within the last 30 days.movieId -> set of userIds
.movieId
s 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.
Sample Input:
Step-by-step:
[101]
Brute-force Approach:
This optimization is possible because we leverage hash sets for constant-time lookups, and avoid unnecessary pairwise comparisons.
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.
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'));