Detect Recurring Transactions with Tunable Tolerances
Learn this problemProblem statement
You are given an array transactions. Each record has the format merchant|YYYY-MM-DD|amountCents, where merchant is a non-empty case-sensitive string, the date is a valid ISO calendar date, and amountCents is a positive integer.
Return the merchants whose transactions form recurring subscriptions under the following rules:
- Records are grouped only when their
merchantstrings are exactly equal. - A group must contain at least
minOccurrencestransactions. - The group's price spread,
maximum amount - minimum amount, must be at mostmaxAmountDifferenceCents. - Sort the group's transactions by date and compute every consecutive gap in calendar days. Let the nominal interval be the lower median gap: after sorting the gaps, use the element at index
(gaps.length - 1) / 2. - Every consecutive gap must differ from the nominal interval by at most
maxIntervalDifferenceDays.
Return each qualifying merchant exactly once in ascending lexicographic order.
Function
findRecurringTransactions(transactions: String[], maxIntervalDifferenceDays: int, maxAmountDifferenceCents: int, minOccurrences: int) → String[]Examples
Example 1
transactions = ["Netflix|2026-01-01|1599","Gym|2026-01-05|5000","Netflix|2026-01-31|1600","Gym|2026-01-12|5000","Netflix|2026-03-02|1598","Gym|2026-01-19|5000"]maxIntervalDifferenceDays = 1maxAmountDifferenceCents = 5minOccurrences = 3return = ["Gym","Netflix"]Gym has two seven-day gaps and no price variation. Netflix has two thirty-day gaps and a price spread of 2 cents. Both satisfy the supplied thresholds, so they are returned in lexicographic order.
Example 2
transactions = ["Cloud|2026-01-01|1000","Cloud|2026-02-01|1300","Cloud|2026-03-01|1000","News|2026-01-10|999","News|2026-02-09|1025","News|2026-03-11|1000"]maxIntervalDifferenceDays = 2maxAmountDifferenceCents = 50minOccurrences = 3return = ["News"]Cloud fails because its price spread is 300 cents. News has a 26-cent spread and equal thirty-day gaps, so it qualifies.
Example 3
transactions = ["Music|2026-01-01|1200","Music|2026-01-31|1200","Music|2026-04-01|1200","Storage|2026-01-15|500","Storage|2026-02-14|500"]maxIntervalDifferenceDays = 3maxAmountDifferenceCents = 0minOccurrences = 3return = []Music has gaps of 30 and 60 days, so one gap is outside the three-day tolerance around the lower-median interval. Storage has fewer than three occurrences.
Constraints
1 <= transactions.length <= 200000- Every record has the exact format
merchant|YYYY-MM-DD|amountCents. - Every date is a valid ISO calendar date from
1970-01-01through9999-12-31. 1 <= amountCents <= 10^90 <= maxIntervalDifferenceDays <= 36500 <= maxAmountDifferenceCents <= 10^92 <= minOccurrences <= transactions.length