Or: why I didn't use Manacher's algorithm and still got 217 new palindromes
The Idea
Everyone knows crafted palindromes like "Never odd or even" or "A man, a plan, a canal – Panama". They're beautiful, but they're constructed — someone sat down and deliberately created them.
What about accidental palindromes? Phrases that appear naturally in everyday speech, journalism, and prose, without anyone trying?
There are accidental meters in prose. There must be accidental palindromes in texts, beyond the reach of word artists.
This is an attempt to find them.
The Data
I used the Russian National Corpus — a massive collection of Russian texts spanning:
- Fiction (19th–21st centuries)
- Journalism (newspapers, magazines)
- Memoirs, letters, diaries
- Scientific and popular science texts
Two separate corpuses were scanned:
| Corpus | Files |
| Main corpus | 131,391 |
| Newspaper corpus | -- |
The Algorithm
Search strategy
I don't search for palindromes at the character level (like Manacher's algorithm does). Instead, I search at the word level:
- Extract all Russian words (Cyrillic only) using regex
- For each position, test sequences of 2–10 consecutive words
- Normalize: remove punctuation, lowercase, replace
ё with е
- Check if the normalized string equals its reverse
- Deduplicate against already found palindromes
Why not Manacher's algorithm?
| Aspect | My approach | Manacher's algorithm |
| Unit | Words | Characters |
| Search space | ≤ 9 sequences per word | Every possible substring |
| Time complexity | O(n × 10) | O(L) |
| Word boundaries respected | ✅ Yes | ❌ No |
| Semantic value | High (phrases) | Low (arbitrary substrings) |
Manacher's algorithm is elegant and linear, but it solves a different problem. I don't need every palindromic substring — I need phrases that make sense. Restricting to 2–10 words makes the naive s == s[::-1] check not just sufficient, but optimal.
Processing 131,391 files on a modern machine took:
Search completed: 2026-08-14 07:51:49.609437
Files processed: 131,391
New palindromes found: 217
Total time: 6:48:27
~6 hours 48 minutes for the entire main corpus. No parallelization, no optimization tricks — just straightforward file-by-file processing. For a one-time research task, this is perfectly acceptable.
Key functions
def normalize_text(text):
"""Remove punctuation, lowercase, convert ё→е"""
cleaned = re.sub(r'[^а-яёА-ЯЁ]', '', text)
cleaned = cleaned.replace('ё', 'е')
return cleaned.lower()
def is_palindrome(word_sequence):
"""Check if a sequence of words forms a palindrome"""
if len(word_sequence) < 2:
return False
# Exclude sequences where all words are identical
if all(w == word_sequence[0] for w in word_sequence):
return False
combined = ''.join(word_sequence)
normalized = normalize_text(combined)
return len(normalized) >= 10 and normalized == normalized[::-1]
Automated pipeline
palindromes.py automatically launches palindromes_paper.py after completion — no manual intervention needed.
Results
Accidental palindromes (with corpus context)
| English translation | Original | Context |
| "more and more and more" | еще и еще и еще | link |
| "to look for a taxi" | искать такси | link |
| "he is right here, but" | он тут как тут но | link |
| "water from ships" | воду с судов | link |
| "in Odessa before" | одессе до | link |
| "and mannequins" | и манекенами | link |
Newspaper corpus finds
| English translation | Original | Note |
| "Ani Lorak, Carolina" | Ани Лорак Каролина | The singer's real name |
| "Sonic in cinema with" | соник в кино с | From a 2019 film review |
| "or infected" | или заразили | Random coincidence in a news text |
Beautiful crafted palindromes (found accidentally in citations)
| English translation | Original |
| "The cat is learned, but how insensitive he is!" | Кот учен, но как он нечуток! |
| "And in the window, Kirichenkova was chirping" | А в окне чирикала Кириченкова |
| "Lenin ate noodles by the sleepers" | У шпал Ленин ел лапшу |
| "Muse, wounded by experience's awl, you will pray to reason" | Муза, ранясь шилом опыта, ты помолишься на разум |
| "You and I are gods and the yoke of existence" | Я и ты — боги и иго бытия |
| "Dante's hell is revealed in deed" | Да вот на деле дантов Ад |
| "The end of marks" | Конец оценок |
Common natural patterns
We found entire classes of palindromes following the same template:
X and X and X
Examples:
еще и еще и еще (more and more and more)
летел и летел (flew and flew)
лил и лил и лил (poured and poured and poured)
Repetitive structures with conjunctions naturally produce palindromic effects.
Post-processing: Cleaning the Noise
The algorithm finds formal palindromes that are semantically meaningless:
е о а а а са а а о е → random letters
м ммм м м м м м м → just 'm' variations
жжж жжж жжж ууу ууу ууу жжж → garbage
э эээ эээээээээ → meaningless
с ссссс сссс ссссс → noise
These were manually removed from the results to keep only meaningful or at least amusing phrases.
Key Takeaways
Accidental palindromes exist — they appear naturally in texts without anyone trying.
Most are short phrases built from repetitive or common words.
Context is everything — some look constructed, but the corpus context proves they're accidental.
Crafted palindromes sneak in anyway — they're quoted in articles, and you can't automatically distinguish them without manual verification.
You don't need Manacher — if you're searching for meaningful phrases, the naive approach with word boundaries works better than character-level algorithms.
Manual cleaning is essential — algorithms find too much noise; semantic filtering requires human judgment.
Links