Risky Fraud Ring
Reported by candidates from Stripe's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
Stripe reported this one in July 2026, and it's Task 3 of a three-part fraud-ring sequence. The whole thing hinges on union-find (disjoint set union), or a graph traversal if you'd rather build adjacency lists. You parse transaction rows, link users who share a device or a credit card, then check whether the target's ring averages strictly above 75. Trusted users with score 0 connect the graph but don't count. If your head goes blank on the setup, StealthCoder runs invisibly on the live OA as a safety net, but the logic here is short enough to hold in your head tonight.
The problem
Stripe Fraud Ring Tasks This problem is Task 3 of 3 in the Stripe fraud-ring sequence. Task 1: Directly Linked Users Task 2: Fraud Ring Size Task 3: Risky Fraud Ring (current) Not every connected cluster is malicious. Each transaction includes a risk score, and a fraud ring should be blocked only when its average risk is high. Each transaction log entry is formatted as user_id,device_id,credit_card,risk_score. Users are connected by shared devices or credit cards. The ring for a target user is the connected component containing that user. A user with risk score 0 is trusted. Trusted users still keep the graph connected, but they are excluded from both the sum and count when computing the average risk score. Return true if the average risk score of non-trusted users in the target ring is strictly greater than 75; otherwise return false. Input transactions: transaction rows formatted as user_id,device_id,credit_card,risk_score. targetUser: the user to investigate. Output Return whether the target ring should be blocked. Function shouldBlockFraudRing(transactions: String[], targetUser: String) → boolean Examples Example 1 transactions = ["Alice,D1,CC1,90","Bob,D1,CC2,0","Charlie,D2,CC2,80"] targetUser = "Alice" return = true The connected ring is Alice, Bob, and Charlie. Bob has score 0, so the average is (90 + 80) / 2 = 85, which is greater than 75. Example 2 transactions = ["Alice,D1,CC1,70","Bob,D1,CC2,0","Charlie,D2,CC2,80"] targetUser = "Alice" return = false Bob still connects Alice and Charlie, but Bob is excluded from the average. (70 + 80) / 2 = 75, and the rule requires strictly greater than 75. Constraints 0 <= transactions.length <= 10^5 0 <= risk_score <= 100 Each user has the same risk score across all their transactions.
Reported by candidates. Source: FastPrep
Pattern and pitfall
Use union-find over user IDs. For each row, union the user with a key for the device and a key for the card, like "D:" + device and "C:" + card, so shared resources merge users automatically. Store each user's score once, since it's the same across rows. Then find the target's root, loop over every user in that root's component, and skip anyone with score 0. Sum the rest and count them. Compare sum > 75 * count, using integers, so you dodge float issues and the strictly-greater edge case from Example 2. The pitfalls are real. Don't drop score-0 users from the graph, because Bob must still bridge Alice and Charlie. Handle a ring where everyone is trusted, since count is 0 and you should return false. If the OA stalls you, StealthCoder is the hedge, reading the problem and giving a working solution live.
The honest play: practice the pattern, and have StealthCoder ready for the one you didn't see coming.
You can drill Risky Fraud Ring cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built for the candidate who saw this exact problem leak two days before his OA and wondered if anyone had a play.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass Stripe's OA.
Stripe reuses patterns across OAs. Built for the candidate who saw this exact problem leak two days before his OA and wondered if anyone had a play. Works on HackerRank, CodeSignal, CoderPad, and Karat.
Risky Fraud Ring FAQ
What's the trick in Risky Fraud Ring?+
Build connectivity first, filter second. Union users through shared device and card keys, including score-0 users, because they act as bridges. Only when computing the average do you exclude them. Mixing those two steps up is the most common way to fail the examples.
Should I use union-find or DFS?+
Either works for up to 10^5 rows. Union-find is shorter: map users, devices, and cards to nodes, union per row, then group by root. DFS needs adjacency lists from device and card to users. Pick whichever you can code without bugs under pressure.
How do I avoid the strictly greater than 75 bug?+
Don't divide. Compare sum > 75 * count with integers. Example 2 gives 150 versus 150, which must return false. A float division can work, but integer comparison removes any rounding doubt and the edge case is explicit.
What if every user in the ring has score 0?+
Then the non-trusted count is 0 and there's no average. Return false instead of dividing by zero. The problem says to return true only when the average is strictly above 75, so an empty set can't qualify.
How do I prepare for this in 48 hours?+
Write union-find with path compression from memory, then solve one connected-components problem with string keys. Then code this one end to end, parsing the comma-separated rows. Test Example 1, Example 2, an all-trusted ring, and a target user in a single-row ring.