/writing/debugging-leetcode.md
How Platforms Detect AI-Generated Competitive Programming Solutions
I am sure you have heard that curiosity kills the cat. Well, in my case it killed my leetcode account for a month - or at least banned my participation in the contests.
You see, during a contest, I converted my own C++ solution into Go and got flagged (How were you so quick leetcode?)
I was just curious: How does a platform decide to flag something? What signals exist? What architecture would you need to evaluate millions of submissions in near real time?
This post is my attempt to reverse-engineer the problem space.
AI-assisted coding is everywhere now. Editors autocomplete entire functions, and a single prompt can produce a working solution to most medium-difficulty contest problems. For competitive programming platforms, this creates an existential integrity problem: a rated contest only means something if the rating reflects the human behind the keyboard.
One disclaimer up front: everything here is speculative analysis. Platforms like LeetCode and Codeforces keep their detection pipelines proprietary (for good reason — publishing the rules tells cheaters exactly how to evade them). What follows is what's plausible given known techniques from plagiarism detection, anti-cheat systems, and ML.
The core problem
At contest scale, a platform must distinguish between three categories of submissions:
- Human-written code — original work, possibly using memorized templates
- Plagiarized code — copied from another contestant or a leaked solution
- AI-generated code — produced partly or fully by an LLM
flowchart TD
S[Incoming submission] --> Q{What is it?}
Q --> H[Human-written<br/>original work + templates]
Q --> P[Plagiarized<br/>copied from a peer or leak]
Q --> A[AI-generated<br/>partly or fully from an LLM]
H --> OK[Accept]
P --> D1[Detectable via<br/>similarity to existing code]
A --> D2[No source artifact —<br/>needs behavioral signals]The first two categories have decades of prior art. The third is new, and it breaks many of the old assumptions. And it all has to work across millions of submissions, dozens of languages, and a hard requirement of low false-positive rates — because every wrong ban is a public trust incident.
Traditional plagiarism detection
Before AI detection, there was code plagiarism detection, and it's worth understanding because modern systems almost certainly build on it.
Token-based matching
The simplest robust approach: normalize the code into a token stream and compare streams instead of raw text. Something like
for (int i = 0; i < n; i++)
becomes
FOR LPAREN TYPE ID ASSIGN NUM SEMI ID LT ID SEMI ID INCR RPAREN
Renaming variables, changing whitespace, or reformatting does nothing — the token stream is identical. Similarity is then computed over token sequences, often using winnowing or k-gram fingerprinting.
AST matching
Token streams can still be fooled by reordering statements or restructuring loops. Abstract Syntax Tree comparison goes a level deeper: two pieces of code with completely different formatting and naming can still have an identical (or near-identical) tree structure. Subtree hashing and tree edit distance let you detect structural clones even after cosmetic transformations.
MOSS-style fingerprinting
Stanford's MOSS (Measure of Software Similarity) is the canonical system here. It uses document fingerprinting — hashing k-grams of normalized code and selecting a representative subset of hashes (winnowing) — so that comparing two submissions is cheap, and comparing one submission against a corpus of millions is feasible. Almost every academic and contest plagiarism system descends from these ideas.
Why AI detection is harder
Here's the catch: all of the above detects similarity to an existing artifact. AI-generated code often has no source artifact to match against.
An LLM asked to solve the same problem twice can produce solutions that:
- use different variable names each time
- structure loops and conditionals differently
- reorder independent logic
- look, superficially, like unique original work
Traditional plagiarism pipelines see a unique submission and wave it through. So detection has to shift from "does this match something?" to "does this behave like a human wrote it?" — which is a much fuzzier question.
Signals platforms likely use
This is the speculative core of the post. Based on what's technically feasible and what anti-cheat systems in other domains do, here are the signal families I'd bet on.
A. Submission timing analysis
Probably the strongest and cheapest signal. Consider this sequence:
flowchart TD
A[Contest starts] --> B[Hard DP problem<br/>solved in 3 minutes]
B --> C{Check history}
C --> D[Rating: never solved<br/>a hard DP before]
C --> E[Solve speed: 10x faster<br/>than personal baseline]
D --> F[⚠ Risk score increases]
E --> FThat's suspicious regardless of what the code looks like. Timing-based anomaly detection likely weighs:
- time from problem release to first submission
- time relative to the contestant's historical solve speed
- solve-order anomalies (skipping easy problems, solving hard ones instantly)
- rating-to-difficulty mismatch
No single timing event is damning, but timing combined with other signals raises a risk score quickly.
B. Cross-language similarity
This one connects directly to my own flag. Suppose a C++ solution is submitted, and shortly after, a Go submission appears with nearly identical algorithmic structure. A naive token matcher sees two different languages and gives up. A more sophisticated pipeline might:
- normalize both into language-agnostic ASTs or IR-like representations
- compare control flow graphs (loop nesting, branch structure, recursion shape)
- compare algorithmic skeletons: same DP state definition, same transition order, same edge-case handling
flowchart LR
CPP[C++ solution] --> P1[C++ parser]
GO[Go solution] --> P2[Go parser]
P1 --> N[Language-agnostic<br/>normalized AST / CFG]
P2 --> N
N --> CMP{Structural<br/>comparison}
CMP -->|High overlap| FLAG[Flag as translation pair]
CMP -->|Low overlap| PASS[Independent solutions]If two submissions in different languages reduce to the same normalized structure, the system can flag them as translations of each other — and it has no way to know whether the translator was the original author, a friend, or an LLM. That ambiguity is exactly where false positives like mine come from.
C. Behavioral analysis
If the contest runs in the platform's own editor, the client can observe how code appears, not just what it says. Plausible telemetry:
- typing cadence and burstiness
- paste events, especially large single pastes
- tab-switch / focus-loss patterns
- long inactivity followed by a sudden complete submission
- whether code is built incrementally (write, run, fix) or appears fully formed
A 200-line solution pasted in one event after four minutes of the tab being unfocused tells a very different story than the same solution typed over forty minutes with intermediate runs. This is classic anti-cheat territory — closer to what game publishers do than to what MOSS does.
sequenceDiagram
participant U as Contestant
participant E as Contest editor
participant T as Telemetry service
U->>E: Opens problem
E->>T: focus event
Note over U,E: Tab loses focus for 4 min
E->>T: blur event (240s)
U->>E: Single paste — 200 lines
E->>T: paste event (size: 200 lines)
U->>E: Submit (no test runs)
T->>T: pattern: blur → large paste → instant submit
T-->>T: ⚠ behavioral anomaly recordedD. AI code style patterns
The weakest signal family, but probably still in the mix. LLM-generated code tends toward:
- unusually clean, uniform structure
- generic variable naming (
result,current,helper) - stereotypical implementations of standard algorithms
- over-commenting, or comments phrased like documentation
- characteristic idioms that show up across many LLM outputs
An ML classifier trained on known-AI vs. known-human contest code could learn these patterns. But style is easy to perturb — rename three variables and delete the comments — so I'd expect platforms to treat stylometry as a supporting signal, never a primary one.
Why false positives happen
This section matters most, because detection systems are judged by their failure modes.
Optimal solutions converge. For many problems there is essentially one right algorithm, and experienced contestants implement it the same way. Independent solutions naturally collide in:
- standard DP formulations
- graph traversal templates
- binary lifting / sparse tables
- segment trees and Fenwick trees
Two strangers writing the canonical segment tree will produce structurally near-identical code. To an AST matcher, that can look exactly like copying.
Templates are legal and universal. Most serious competitors maintain personal libraries. Shared template ancestry (often from the same popular blog posts) creates similarity that has nothing to do with cheating.
Self-translation looks like collusion. Translating your own accepted solution into another language — for practice, for performance, or just for fun — produces precisely the cross-language structural match described above. The system sees a translation; it cannot see authorship.
Any honest pipeline has to accept that similarity signals are inherently noisy, which is why the architecture matters.
A system design perspective
If I had to architect this, the pipeline would look something like:
flowchart TD
S[Submissions] --> PS[Parsing Service<br/>language-specific frontends]
PS --> TK[Tokenization /<br/>AST normalization]
TK --> FE[Feature Extraction<br/>fingerprints · CFG features · embeddings]
FE --> SE[Similarity Engine<br/>corpus-wide ANN search]
BT[Editor telemetry<br/>timing · paste · focus] --> BJ[Behavioral Signal Join]
SE --> BJ
UH[(User history /<br/>feature store)] --> ML
BJ --> ML[ML Risk Scoring]
ML --> FP{Score above<br/>threshold?}
FP -->|Yes| HR[Human review queue]
FP -->|No| OK[Accept]
HR -->|Confirmed| ACT[Action: penalty / ban]
HR -->|Cleared| OKA few design notes:
- Nothing should auto-ban on a single signal. The risk scorer combines similarity, timing, behavior, and history; only high combined scores reach human reviewers.
- The similarity engine is the hard part. Comparing each new submission against every prior submission is O(n²) and impossible naively. Fingerprint hashing and approximate nearest-neighbor (ANN) indexes make it tractable: each submission becomes a compact vector or hash set, and lookups hit a pre-built index instead of the raw corpus.
- Feature stores keep per-user behavioral baselines (typical solve speed, typing patterns) so anomaly detection compares you against yourself, not just the population.
Scaling challenges
The engineering problems here are genuinely interesting:
- Volume: a large contest produces hundreds of thousands of submissions in two hours, all needing near-real-time scoring.
- Index freshness: the similarity index must include submissions from this contest, not just historical ones — copying happens live.
- Cross-language indexing: normalized representations must be comparable across a dozen-plus languages, which means maintaining a parser frontend for each.
- Graph-scale collusion detection: pairwise similarity becomes a graph problem — clusters of mutually similar submissions indicate leaked solutions, and finding dense clusters across millions of nodes is its own distributed-systems challenge.
flowchart LR
subgraph CL[Leaked solution cluster]
A((User A)) --- B((User B))
B --- C((User C))
A --- C
C --- D((User D))
A --- D
B --- D
end
E((User E)) --- F((User F))
G((User G))
classDef suspect fill:#e05c5c,stroke:#a33,color:#fff
class A,B,C,D suspect
style CL fill:transparent,stroke:#e05c5c,stroke-dasharray:4A dense clique of mutually similar submissions (left) is a much stronger signal than any single pairwise match — one shared template explains an edge, but rarely a clique.
Embedding models add a modern twist here. Models like CodeBERT and GraphCodeBERT map code into dense vectors where semantically similar programs land close together — even across languages. A vector index over submission embeddings could catch "same algorithm, different surface form" cases that token and AST methods miss. Whether contest platforms actually run this in production is unknown, but the pieces all exist off the shelf.
The ethical gray zone
Detection capability forces uncomfortable policy questions:
- Is AI-assisted translation of your own solution cheating, when the algorithm is entirely yours?
- If an LLM only fixes syntax errors, has the contestant cheated?
- Where exactly is the line between an autocomplete suggestion and a generated solution?
- Should contests evolve into explicitly AI-augmented formats, the way chess split into engine-assisted and classical play?
Right now, platforms answer these questions implicitly, through opaque flagging systems — which means contestants discover the rules only by tripping over them.
Closing thought
AI-assisted programming is becoming unavoidable. The interesting problem is no longer whether AI is used, but how platforms distinguish assistance from abuse while keeping false positives — the honest contestant translating their own code, the two strangers who wrote the same canonical segment tree — from becoming collateral damage. That's not a moderation problem. It's a systems problem, and it's far from solved.