The problem in brief
A journal has been caught publishing a nonsense paper produced by an old automatic paper generator, the kind that strings together grammatical but meaningless computer-science sentences ("we disconfirm that the Turing machine and Smalltalk are largely incompatible"). Given the plain text of a paper, your program must print "Fake" or "Real". You get 10 generated papers and 50 real ones (arXiv papers in physics, mathematics and computer science, converted to text) as examples, and the hidden test papers are "comparable". It is from the Swedish AI Olympiad final 2025 (by Joshua Andersson and Rasmus Hulthe). Your program runs on Kattis on each paper, with a 1-second limit.
The test papers are hidden, so we evaluate by leave-one-out on the 60 examples: every fake paper is judged by a detector built without it.
How you are scored
With the fraction of fake papers caught and the fraction of real papers recognised as real,
The exponent punishes false accusations: missing one fake out of 10 costs 10 %, but wrongly flagging 1 real paper out of 50 costs , and flagging 5 of them costs 23 %. The provided baseline answers "Fake" every time, so and it scores 0.
What to aim for. 100. The Kattis statistics show four full solves, then 84.2 and 84.1.
First look at the data
- The fake papers are 9 500 to 12 500 characters long. The real ones range from 6 500 to 90 000. Length alone ("short means fake") gives at best , , which scores 44.
- The fake papers read like a single author with a tiny vocabulary: "epistemologies", "our heuristic", "the Internet", "Smalltalk", "red-black trees", "we confirm that...". They also always have the same section skeleton (Introduction, Related Work, Implementation, Evaluation, Conclusion). The real papers cover every field, with formulas, names and domain jargon.
- All 10 fakes put together use only 2 180 distinct words. That is the fingerprint of a grammar-based generator: it fills in templates from fixed word lists, so its outputs keep recycling the same words and, above all, the same word pairs.
A first baseline
A text classifier works out of the box. TF-IDF features plus logistic regression (balanced class weights), evaluated by leave-one-out, separates all 60 papers. The fakes get a probability of 0.89 to 0.93 and the most suspicious real paper 0.15, so the score is 100. But with 10 positive examples, a 600-dimensional linear model learnt from them is a black box whose behaviour on a new kind of real paper (a computer-science systems paper, say) is hard to predict. We want a rule whose safety margin we can measure and explain.
The key idea
A generator with a closed vocabulary reuses its own phrases. Collect every pair of consecutive words (bigram) that occurs in the known fake papers, about 8 100 of them. For any paper, measure its bigram coverage:
A generated paper is built from the same templates and word lists, so most of its word pairs have been seen before. A real paper only shares the generic ones ("of the", "in this", "we show"). Why bigrams rather than single words? Individual words are shared widely: even real papers have 56 % to 76 % of their words in the fake vocabulary, against 93 % to 96 % for fakes. Pairs are far more specific to the generator's templates, and the gap widens from a factor 1.2 to a factor 3.

With leave-one-out (each fake paper measured against the bigrams of the 9 others):
- fake papers: between 0.619 and 0.680;
- real papers: between 0.108 and 0.209.
We put the threshold at the geometric middle of the gap, . That leaves a safety factor of about 1.7 on each side.
Does it generalise? The right panel builds the bigram set from only of the fake papers and measures the coverage of the unseen ones. With , the worst unseen fake still has 0.33 against 0.15 for the most fake-looking real paper. Coverage of new fakes grows steadily with , while real papers barely move. With all 10 fakes, which is what the submission uses, a new generated paper should land above 0.6, far from the threshold. This is the main argument that the hidden tests will behave the same way.
The full solution
- Lower-case the text and keep runs of letters as words (the PDF-to-text conversion leaves hyphenations and stray numbers, which do no harm).
- Build the set of word bigrams of the 10 fake papers.
- Compute the coverage of the input paper and answer "Fake" if .
- For Kattis, the bigram set is zlib-compressed and base85-encoded inside a 40 KiB program. The whole computation is a single pass over the words, well under the 1-second limit even for a 300 000-character paper.
w = re.findall(r'[a-z]+', input().lower())
b = [w[i] + ' ' + w[i + 1] for i in range(len(w) - 1)]
print('Fake' if sum(x in K for x in b) / max(1, len(b)) > 0.36 else 'Real')
Results
Leave-one-out on the 60 example papers (the Kattis test papers are hidden).
| approach | TP | TN | score |
|---|---|---|---|
| always "Fake" (baseline.py) | 1.00 | 0.00 | 0 |
| "Fake" if shorter than 13 000 characters | 1.00 | 0.72 | 44 |
| word coverage (any threshold between 0.76 and 0.93) | 1.00 | 1.00 | 100 |
| TF-IDF + logistic regression | 1.00 | 1.00 | 100 |
| bigram coverage (final) | 1.00 | 1.00 | 100 |
Several methods reach 100 here. We chose bigram coverage because it has the widest, best-understood margin (a factor 3), it degrades gracefully when fewer examples of the generator are available, and it is one line of logic.
What to remember
- When text comes from a template generator, its closed vocabulary is its signature. Measure how much of a document is made of "already seen" pieces.
- Word pairs (n-grams) are much more specific than single words.
- With an asymmetric score (), place the threshold to maximise the margin on the costly side, and check that margin rather than just the accuracy.
- With tiny data, evaluate by leave-one-out and stress-test generalisation by shrinking the training set, as in the right panel.
- Prefer a simple rule you can reason about over a black box that happens to be perfect on 60 examples.