#3832
Hard Database Find users with persistent behavior patterns
65.6% acceptance
Mar 16, 2026
23
1
Table: activity
+--------------+---------+
| Column Name | Type |
+--------------+---------+
| user_id | int |
| action_date | date |
| action | varchar |
+--------------+---------+
(user_id, action_date, action) is the primary key (unique value) for this table.
Each row represents a user performing a specific action on a given date.
Write a solution to identify behaviorally stable users based on the following definition:
A user is considered behaviorally stable if there exists a sequence of at least 5 consecutive days such that:
The user performed exactly one action per day during that period.
The action is the same on all those consecutive days.
If a user has multiple qualifying sequences, only consider the sequence with the maximum length.
Return the result table ordered by streak_length in descending order, then by user_id in ascending order.
The result format is in the following example.
Solution
Pandas
Time O(1)
Space O(1)
# Table: activity
#
# +--------------+---------+
# | Column Name | Type |
# +--------------+---------+
# | user_id | int |
# | action_date | date |
# | action | varchar |
# +--------------+---------+
# (user_id, action_date, action) is the primary key (unique value) for this table.
# Each row represents a user performing a specific action on a given date.
#
# Write a solution to identify behaviorally stable users based on the following definition:
#
# A user is considered behaviorally stable if there exists a sequence of at least 5 consecutive days such that:
#
# The user performed exactly one action per day during that period.
#
# The action is the same on all those consecutive days.
#
# If a user has multiple qualifying sequences, only consider the sequence with the maximum length.
#
# Return the result table ordered by streak_length in descending order, then by user_id in ascending order.
#
# The result format is in the following example.
#
# Example 1:
# Input:
# activity table:
# +---------+-------------+--------+
# | user_id | action_date | action |
# +---------+-------------+--------+
# | 1 | 2024-01-01 | login |
# | 1 | 2024-01-02 | login |
# | 1 | 2024-01-03 | login |
# | 1 | 2024-01-04 | login |
# | 1 | 2024-01-05 | login |
# | 1 | 2024-01-06 | logout |
# | 2 | 2024-01-01 | click |
# | 2 | 2024-01-02 | click |
# | 2 | 2024-01-03 | click |
# | 2 | 2024-01-04 | click |
# | 3 | 2024-01-01 | view |
# | 3 | 2024-01-02 | view |
# | 3 | 2024-01-03 | view |
# | 3 | 2024-01-04 | view |
# | 3 | 2024-01-05 | view |
# | 3 | 2024-01-06 | view |
# | 3 | 2024-01-07 | view |
# +---------+-------------+--------+
# Output:
# +---------+--------+---------------+------------+------------+
# | user_id | action | streak_length | start_date | end_date |
# +---------+--------+---------------+------------+------------+
# | 3 | view | 7 | 2024-01-01 | 2024-01-07 |
# | 1 | login | 5 | 2024-01-01 | 2024-01-05 |
# +---------+--------+---------------+------------+------------+
# Explanation:
# User 1:
# Performed login from 2024-01-01 to 2024-01-05 on consecutive days
# Each day has exactly one action, and the action is the same
# Streak length = 5 (meets minimum requirement)
# The action changes on 2024-01-06, ending the streak
# User 2:
# Performed click for only 4 consecutive days
# Does not meet the minimum streak length of 5
# Excluded from the result
# User 3:
# Performed view for 7 consecutive days
# This is the longest valid sequence for this user
# Included in the result
# The Results table is ordered by streak_length in descending order, then by user_id in ascending order
import pandas as pd
def find_behaviorally_stable_users(activity: pd.DataFrame) -> pd.DataFrame:
# Count actions per user per day; keep only days with exactly one action
daily = activity.groupby(['user_id', 'action_date']).agg(
action_count=('action', 'count'),
action=('action', 'first')
).reset_index()
daily = daily[daily['action_count'] == 1].copy()
daily['action_date'] = pd.to_datetime(daily['action_date'])
daily = daily.sort_values(['user_id', 'action_date'])
# Detect streak breaks: either different action or non-consecutive date
daily['prev_date'] = daily.groupby('user_id')['action_date'].shift(1)
daily['prev_action'] = daily.groupby('user_id')['action'].shift(1)
daily['is_break'] = (
(daily['action_date'] - daily['prev_date'] != pd.Timedelta(days=1)) |
(daily['action'] != daily['prev_action'])
)
daily['is_break'] = daily['is_break'].fillna(True)
daily['streak_id'] = daily.groupby('user_id')['is_break'].cumsum()
streaks = daily.groupby(['user_id', 'streak_id', 'action']).agg(
streak_length=('action_date', 'count'),
start_date=('action_date', 'min'),
end_date=('action_date', 'max')
).reset_index()
streaks = streaks[streaks['streak_length'] >= 5]
if streaks.empty:
return pd.DataFrame(columns=['user_id', 'action', 'streak_length', 'start_date', 'end_date'])
# Keep only the longest streak per user
best = streaks.loc[streaks.groupby('user_id')['streak_length'].idxmax()]
return best[['user_id', 'action', 'streak_length', 'start_date', 'end_date']].sort_values(
['streak_length', 'user_id'], ascending=[False, True]
).reset_index(drop=True)