Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using core interview patterns strategy.
Table: UserActivity
+------------------+---------+
| Column Name | Type |
+------------------+---------+
| user_id | int |
| activity_date | date |
| activity_type | varchar |
| activity_duration| int |
+------------------+---------+
(user_id, activity_date, activity_type) is the unique key for this table.
activity_type is one of ('free_trial', 'paid', 'cancelled').
activity_duration is the number of minutes the user spent on the platform that day.
Each row represents a user's activity on a specific date.
A subscription service wants to analyze user behavior patterns. The company offers a 7-day free trial, after which users can subscribe to a paid plan or cancel. Write a solution to:
2 decimal places)2 decimal places)Return the result table ordered by user_id in ascending order.
The result format is in the following example.
Example:
Input:
UserActivity table:
+---------+---------------+---------------+-------------------+ | user_id | activity_date | activity_type | activity_duration | +---------+---------------+---------------+-------------------+ | 1 | 2023-01-01 | free_trial | 45 | | 1 | 2023-01-02 | free_trial | 30 | | 1 | 2023-01-05 | free_trial | 60 | | 1 | 2023-01-10 | paid | 75 | | 1 | 2023-01-12 | paid | 90 | | 1 | 2023-01-15 | paid | 65 | | 2 | 2023-02-01 | free_trial | 55 | | 2 | 2023-02-03 | free_trial | 25 | | 2 | 2023-02-07 | free_trial | 50 | | 2 | 2023-02-10 | cancelled | 0 | | 3 | 2023-03-05 | free_trial | 70 | | 3 | 2023-03-06 | free_trial | 60 | | 3 | 2023-03-08 | free_trial | 80 | | 3 | 2023-03-12 | paid | 50 | | 3 | 2023-03-15 | paid | 55 | | 3 | 2023-03-20 | paid | 85 | | 4 | 2023-04-01 | free_trial | 40 | | 4 | 2023-04-03 | free_trial | 35 | | 4 | 2023-04-05 | paid | 45 | | 4 | 2023-04-07 | cancelled | 0 | +---------+---------------+---------------+-------------------+
Output:
+---------+--------------------+-------------------+ | user_id | trial_avg_duration | paid_avg_duration | +---------+--------------------+-------------------+ | 1 | 45.00 | 76.67 | | 3 | 70.00 | 63.33 | | 4 | 37.50 | 45.00 | +---------+--------------------+-------------------+
Explanation:
The result table only includes users who converted from free trial to paid subscription (users 1, 3, and 4), and is ordered by user_id in ascending order.
Problem summary: Table: UserActivity +------------------+---------+ | Column Name | Type | +------------------+---------+ | user_id | int | | activity_date | date | | activity_type | varchar | | activity_duration| int | +------------------+---------+ (user_id, activity_date, activity_type) is the unique key for this table. activity_type is one of ('free_trial', 'paid', 'cancelled'). activity_duration is the number of minutes the user spent on the platform that day. Each row represents a user's activity on a specific date. A subscription service wants to analyze user behavior patterns. The company offers a 7-day free trial, after which users can subscribe to a paid plan or cancel. Write a solution to: Find users who converted from free trial to paid subscription Calculate each user's average daily activity duration during their free trial period (rounded to 2 decimal places) Calculate each user's average
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: General problem-solving
{"headers":{"UserActivity":["user_id","activity_date","activity_type","activity_duration"]},"rows":{"UserActivity":[[1,"2023-01-01","free_trial",45],[1,"2023-01-02","free_trial",30],[1,"2023-01-05","free_trial",60],[1,"2023-01-10","paid",75],[1,"2023-01-12","paid",90],[1,"2023-01-15","paid",65],[2,"2023-02-01","free_trial",55],[2,"2023-02-03","free_trial",25],[2,"2023-02-07","free_trial",50],[2,"2023-02-10","cancelled",0],[3,"2023-03-05","free_trial",70],[3,"2023-03-06","free_trial",60],[3,"2023-03-08","free_trial",80],[3,"2023-03-12","paid",50],[3,"2023-03-15","paid",55],[3,"2023-03-20","paid",85],[4,"2023-04-01","free_trial",40],[4,"2023-04-03","free_trial",35],[4,"2023-04-05","paid",45],[4,"2023-04-07","cancelled",0]]}}Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3497: Analyze Subscription Conversion
// Auto-generated Java example from py.
class Solution {
public void exampleSolution() {
}
}
// Reference (py):
// # Accepted solution for LeetCode #3497: Analyze Subscription Conversion
// import pandas as pd
//
//
// def analyze_subscription_conversion(user_activity: pd.DataFrame) -> pd.DataFrame:
// df = user_activity[user_activity["activity_type"] != "cancelled"]
//
// df_grouped = (
// df.groupby(["user_id", "activity_type"])["activity_duration"]
// .mean()
// .add(0.0001)
// .round(2)
// .reset_index()
// )
//
// df_free_trial = (
// df_grouped[df_grouped["activity_type"] == "free_trial"]
// .rename(columns={"activity_duration": "trial_avg_duration"})
// .drop(columns=["activity_type"])
// )
//
// df_paid = (
// df_grouped[df_grouped["activity_type"] == "paid"]
// .rename(columns={"activity_duration": "paid_avg_duration"})
// .drop(columns=["activity_type"])
// )
//
// result = df_free_trial.merge(df_paid, on="user_id", how="inner").sort_values(
// "user_id"
// )
//
// return result
// Accepted solution for LeetCode #3497: Analyze Subscription Conversion
// Auto-generated Go example from py.
func exampleSolution() {
}
// Reference (py):
// # Accepted solution for LeetCode #3497: Analyze Subscription Conversion
// import pandas as pd
//
//
// def analyze_subscription_conversion(user_activity: pd.DataFrame) -> pd.DataFrame:
// df = user_activity[user_activity["activity_type"] != "cancelled"]
//
// df_grouped = (
// df.groupby(["user_id", "activity_type"])["activity_duration"]
// .mean()
// .add(0.0001)
// .round(2)
// .reset_index()
// )
//
// df_free_trial = (
// df_grouped[df_grouped["activity_type"] == "free_trial"]
// .rename(columns={"activity_duration": "trial_avg_duration"})
// .drop(columns=["activity_type"])
// )
//
// df_paid = (
// df_grouped[df_grouped["activity_type"] == "paid"]
// .rename(columns={"activity_duration": "paid_avg_duration"})
// .drop(columns=["activity_type"])
// )
//
// result = df_free_trial.merge(df_paid, on="user_id", how="inner").sort_values(
// "user_id"
// )
//
// return result
# Accepted solution for LeetCode #3497: Analyze Subscription Conversion
import pandas as pd
def analyze_subscription_conversion(user_activity: pd.DataFrame) -> pd.DataFrame:
df = user_activity[user_activity["activity_type"] != "cancelled"]
df_grouped = (
df.groupby(["user_id", "activity_type"])["activity_duration"]
.mean()
.add(0.0001)
.round(2)
.reset_index()
)
df_free_trial = (
df_grouped[df_grouped["activity_type"] == "free_trial"]
.rename(columns={"activity_duration": "trial_avg_duration"})
.drop(columns=["activity_type"])
)
df_paid = (
df_grouped[df_grouped["activity_type"] == "paid"]
.rename(columns={"activity_duration": "paid_avg_duration"})
.drop(columns=["activity_type"])
)
result = df_free_trial.merge(df_paid, on="user_id", how="inner").sort_values(
"user_id"
)
return result
// Accepted solution for LeetCode #3497: Analyze Subscription Conversion
// Rust example auto-generated from py reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (py):
// # Accepted solution for LeetCode #3497: Analyze Subscription Conversion
// import pandas as pd
//
//
// def analyze_subscription_conversion(user_activity: pd.DataFrame) -> pd.DataFrame:
// df = user_activity[user_activity["activity_type"] != "cancelled"]
//
// df_grouped = (
// df.groupby(["user_id", "activity_type"])["activity_duration"]
// .mean()
// .add(0.0001)
// .round(2)
// .reset_index()
// )
//
// df_free_trial = (
// df_grouped[df_grouped["activity_type"] == "free_trial"]
// .rename(columns={"activity_duration": "trial_avg_duration"})
// .drop(columns=["activity_type"])
// )
//
// df_paid = (
// df_grouped[df_grouped["activity_type"] == "paid"]
// .rename(columns={"activity_duration": "paid_avg_duration"})
// .drop(columns=["activity_type"])
// )
//
// result = df_free_trial.merge(df_paid, on="user_id", how="inner").sort_values(
// "user_id"
// )
//
// return result
// Accepted solution for LeetCode #3497: Analyze Subscription Conversion
// Auto-generated TypeScript example from py.
function exampleSolution(): void {
}
// Reference (py):
// # Accepted solution for LeetCode #3497: Analyze Subscription Conversion
// import pandas as pd
//
//
// def analyze_subscription_conversion(user_activity: pd.DataFrame) -> pd.DataFrame:
// df = user_activity[user_activity["activity_type"] != "cancelled"]
//
// df_grouped = (
// df.groupby(["user_id", "activity_type"])["activity_duration"]
// .mean()
// .add(0.0001)
// .round(2)
// .reset_index()
// )
//
// df_free_trial = (
// df_grouped[df_grouped["activity_type"] == "free_trial"]
// .rename(columns={"activity_duration": "trial_avg_duration"})
// .drop(columns=["activity_type"])
// )
//
// df_paid = (
// df_grouped[df_grouped["activity_type"] == "paid"]
// .rename(columns={"activity_duration": "paid_avg_duration"})
// .drop(columns=["activity_type"])
// )
//
// result = df_free_trial.merge(df_paid, on="user_id", how="inner").sort_values(
// "user_id"
// )
//
// return result
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.