Prerequisites

Before attempting this problem, you should be comfortable with:

  • Hash Maps - Used to group website visits by user and count pattern frequencies
  • Sets - Used to avoid counting duplicate patterns from the same user
  • Sorting - Required to process visits in chronological order
  • Combinatorics (Triple Nested Loops) - Generating all 3-element subsequences from a list

1. Hash Map

Intuition

We need to find the most common 3-website pattern visited by users in sequential order. The key insight is that we should group each user's website visits in chronological order, then generate all possible 3-site combinations for each user. By counting how many distinct users visit each pattern, we can find the most popular one.

Since we want patterns across different users (not repeated visits by the same user), we use a set for each user's patterns before counting. This ensures each user contributes at most once to each pattern's count.

Algorithm

  1. Sort all visits by timestamp to ensure chronological order.
  2. Group websites by user, maintaining the order of visits.
  3. For each user, generate all possible 3-website patterns using three nested loops i, j, k. Store patterns in a set to avoid counting duplicates from the same user.
  4. Count how many users have each pattern.
  5. Return the pattern with the highest count, breaking ties lexicographically.
class Solution:
    def mostVisitedPattern(self, username: List[str], timestamp: List[int], website: List[str]) -> List[str]:
        arr = list(zip(timestamp, username, website))
        arr.sort()

        mp = defaultdict(list)
        for time, user, site in arr:
            mp[user].append(site)

        count = defaultdict(int)
        for user in mp:
            patterns = set()
            cur = mp[user]
            for i in range(len(cur)):
                for j in range(i + 1, len(cur)):
                    for k in range(j + 1, len(cur)):
                        patterns.add((cur[i], cur[j], cur[k]))
            for p in patterns:
                count[p] += 1

        max_count = 0
        res = tuple()
        for pattern in count:
            if count[pattern] > max_count or (count[pattern] == max_count and pattern < res):
                max_count = count[pattern]
                res = pattern

        return list(res)

Time & Space Complexity

  • Time complexity: O(nlogn+nu+n3w)O(n \log n + n * u + n ^ 3 * w)
  • Space complexity: O(nu+n3w)O(n * u + n ^ 3 * w)

Where nn is the size of the array timestamptimestamp, uu is the maximum length of any string in the array usernameusername, and ww is the maximum length of any string in the array websitewebsite.


Common Pitfalls

Counting Duplicate Patterns From the Same User

When a user visits enough websites to generate the same 3-site pattern multiple times, you should only count that pattern once per user. Failing to use a set for each user's patterns will inflate the count incorrectly.

# Wrong: counting duplicates from same user
for i in range(len(cur)):
    for j in range(i + 1, len(cur)):
        for k in range(j + 1, len(cur)):
            count[(cur[i], cur[j], cur[k])] += 1  # Same user counted multiple times

# Correct: use set per user first
patterns = set()
for i, j, k in combinations:
    patterns.add((cur[i], cur[j], cur[k]))
for p in patterns:
    count[p] += 1

Not Sorting Visits by Timestamp Before Grouping

The pattern must follow chronological order of visits. If you group websites by user without first sorting by timestamp, the patterns will be in arbitrary order and produce wrong results.

# Wrong: not sorting by timestamp
for i in range(n):
    mp[username[i]].append(website[i])  # Order depends on input, not time

# Correct: sort first
arr.sort(key=lambda x: x[0])  # Sort by timestamp

Incorrect Lexicographic Comparison for Tie-Breaking

When multiple patterns have the same count, you must return the lexicographically smallest one. Comparing concatenated strings with a separator may not give correct lexicographic ordering of the tuple components.

# Wrong: comparing by concatenated string may differ from tuple comparison
if "a#z#b" < "aa#a#a":  # String comparison, not tuple comparison

# Correct: compare as tuples or ensure proper tuple-based comparison
if pattern < res:  # Where pattern and res are tuples