Problem · Hash Table

Detect Recurring Transactions with Tunable Tolerances

Learn this problem
MediumRampFULLTIMEONSITE INTERVIEW

Problem 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 merchant strings are exactly equal.
  • A group must contain at least minOccurrences transactions.
  • The group's price spread, maximum amount - minimum amount, must be at most maxAmountDifferenceCents.
  • 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-01 through 9999-12-31.
  • 1 <= amountCents <= 10^9
  • 0 <= maxIntervalDifferenceDays <= 3650
  • 0 <= maxAmountDifferenceCents <= 10^9
  • 2 <= minOccurrences <= transactions.length

More Ramp problems

drafts saved locally
public String[] findRecurringTransactions(String[] transactions, int maxIntervalDifferenceDays, int maxAmountDifferenceCents, int minOccurrences) {
    // write your code here
}
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"]
maxIntervalDifferenceDays1
maxAmountDifferenceCents5
minOccurrences3
expected["Gym", "Netflix"]
checking account