Palindrom to wyraz, zdanie albo liczba, które czytane od końca brzmią tak samo jak od początku, na przykład kajak, potop albo zdanie “Kobyła ma mały bok”. W Pythonie sprawdzisz to jedną linijką kodu. Przy polskich literach łatwo jednak o fałszywy wynik, jeśli program porównuje bajty zamiast znaków.
Palindrom: co to jest
Słownik języka polskiego PWN definiuje palindrom jako “wyraz lub zdanie brzmiące tak samo przy czytaniu od lewej strony do prawej, jak i odwrotnie, np. kajak”. Nazwa pochodzi od greckiego słowa palíndromos, czyli “biegnący na powrót”.
W praktyce spotkasz cztery rodzaje palindromów:
- wyrazowe: kajak, potop, Anna, sos;
- zdaniowe: “Kobyła ma mały bok”, “Zakopane na pokaz”;
- liczbowe: 121, 1001, 12321;
- daty: 22.02.2022 albo 03.02.2030 w zapisie dzień, miesiąc, rok.
W palindromach zdaniowych zwyczajowo pomija się spacje, znaki interpunkcyjne i wielkość liter. Liczy się sam ciąg liter. Dlatego “Ile Romanowi dała za ład Iwona moreli?” jest palindromem, choć słowa czytane wspak tworzą zupełnie inne podziały. Ta umowa ma znaczenie dla programisty: kod musi najpierw oczyścić tekst, a dopiero potem porównać go z odwróconą wersją.
Palindromy: przykłady polskich wyrazów i zdań
Każdy przykład z tabeli sprawdziliśmy funkcją z sekcji o Pythonie (Python 3.14.3). Kilka zdań pochodzi z części krzyżówkowej tego hasła w SJP PWN.
| Przykład | Rodzaj | Czytany wspak daje to samo? |
|---|---|---|
| kajak, potop, Anna | wyraz | tak |
| Kobyła ma mały bok. | zdanie | tak |
| Ile Romanowi dała za ład Iwona moreli? | zdanie | tak |
| Akta generała ma mała renegatka. | zdanie | tak |
| Łapał za kran, a kanarka złapał. | zdanie | tak |
| Zakopane na pokaz. | zdanie | tak |
| Może jutro ta dama da tortu jeżom. | zdanie | tak |
| popija tutaj i pop | zdanie (SJP PWN) | tak |
| zaraza zaraz | zdanie (SJP PWN) | tak |
| zagwiżdż i w gaz | zdanie (SJP PWN) | tak |
| Elf układa kufle. | zdanie | nie |

Ostatni wiersz tabeli jest przestrogą. Zdanie “Elf układa kufle” krąży w sieci jako przykład palindromu, ale czytane litera po literze od końca daje “elfukadałkufle”. Litera “ł” stoi po złej stronie środka. Zanim wpiszesz palindrom do pracy domowej albo do krzyżówki, sprawdź go kodem albo choćby wypisz litery na kartce.
Pierwsza Misja AI · Kodożercy
Pierwszy raz z AI? Zaczynasz od zera.
Pierwsza Misja AI to kurs dla osób bez technicznego przygotowania. Nie musisz pisać kodu ani znać żargonu. Dowiesz się, jak działa AI, jak promptować i jak korzystać z niej w pracy.
Wejdź na pokład →
Palindromy liczbowe i daty
Liczba jest palindromem, gdy jej cyfry czytane od końca dają tę samą liczbę: 7, 121, 1001, 12321. Każda liczba jednocyfrowa jest palindromem. Liczba 10 nim nie jest, bo od końca daje 01, czyli 1.
Liczbę da się sprawdzić bez zamiany na tekst. Wystarczy odwrócić ją arytmetycznie, cyfra po cyfrze:
def odwroc_liczbe(n):
wynik = 0
while n > 0:
wynik = wynik * 10 + n % 10
n //= 10
return wynik
for n in (12321, 1221, 123, 7, 10):
print(n, odwroc_liczbe(n) == n)
# 12321 True
# 1221 True
# 123 False
# 7 True
# 10 False
Działanie n % 10 daje ostatnią cyfrę, a n //= 10 ją odcina. Ten sam pomysł rozbierania liczby na cyfry wraca przy liczbach pierwszych i sicie Eratostenesa i przy zamianie na cyfry rzymskie.
Daty-palindromy do 2099 roku
Data jest palindromem, gdy zapis DD.MM.RRRR bez kropek czyta się tak samo od końca. Nasz skrypt sprawdził każdy dzień od 1 stycznia 2000 do 31 grudnia 2099. Takich dat jest 29 i wszystkie wypadają w lutym. Ostatnia była 22.02.2022, następna będzie 3 lutego 2030 roku (03.02.2030).
Luty bierze się z arytmetyki. Rok 20xy czytany od końca zaczyna się od “yx02”. Pierwsze dwie cyfry to dzień, a “02” to miesiąc. Dzień “yx” musi przy tym istnieć w lutym. Dlatego lista kończy się na 29.02.2092: rok 2092 jest przestępny, a rok 2093 dałby nieistniejący 39 lutego.

Jak sprawdzić palindrom w Pythonie
Do pojedynczego wyrazu wystarczy porównanie napisu z jego odwróconą kopią. Do zdań potrzebna jest jeszcze normalizacja tekstu. Poniżej trzy wersje, każda uruchomiona w Pythonie 3.14.3.
Najkrótsza wersja: s == s[::-1]
Wycinek [::-1] tworzy kopię napisu od końca. Porównanie sprawdza, czy obie wersje są równe:
def czy_palindrom(s):
return s == s[::-1]
print(czy_palindrom("kajak")) # True
print(czy_palindrom("Kajak")) # False
print(czy_palindrom("abc")) # False
Wynik dla “Kajak” to False, bo wielka “K” i mała “k” to dla Pythona różne znaki. Ta wersja nadaje się więc tylko do tekstu, który już jest oczyszczony.
Dwa wskaźniki: bez kopiowania napisu
Druga wersja porównuje litery parami: pierwszą z ostatnią, drugą z przedostatnią, aż wskaźniki spotkają się w środku. Nie tworzy kopii napisu i kończy pracę przy pierwszej niezgodności:
def czy_palindrom_wskazniki(s):
i, j = 0, len(s) - 1
while i < j:
if s[i] != s[j]:
return False
i += 1
j -= 1
return True
Obie wersje mają złożoność liniową, czyli O(n): czas rośnie proporcjonalnie do długości tekstu. Wersja ze wskaźnikami zużywa jednak stałą ilość dodatkowej pamięci. Taki sam schemat da się narysować jako pętlę na schemacie blokowym.
Zdania: casefold(), isalnum() i normalizacja NFC
Dla zdań trzeba usunąć spacje i interpunkcję oraz ujednolicić wielkość liter. Ta funkcja sprawdziła wszystkie przykłady z tabeli wyżej:
import unicodedata
def czy_palindrom_zdanie(tekst):
tekst = unicodedata.normalize("NFC", tekst)
znaki = [z for z in tekst.casefold() if z.isalnum()]
return znaki == znaki[::-1]
print(czy_palindrom_zdanie("Kobyła ma mały bok.")) # True
print(czy_palindrom_zdanie("Ile Romanowi dała za ład Iwona moreli?")) # True
print(czy_palindrom_zdanie("Elf układa kufle.")) # False
Metoda casefold() według dokumentacji Pythona usuwa różnice wielkości liter skuteczniej niż lower(). Na przykład niemieckie “ß” zamienia na “ss”. Metoda isalnum() przepuszcza tylko litery i cyfry, więc znika interpunkcja i spacje. Po co jest normalizacja NFC, wyjaśnia następna sekcja.
Polskie znaki: kiedy “Kobyła ma mały bok” przestaje być palindromem
Polskie litery psują sprawdzanie palindromu wtedy, gdy program porównuje bajty zamiast znaków. W kodowaniu UTF-8 litera “ł” zajmuje dwa bajty: C5 82. Napis “kobyłamamałybok” ma więc 15 znaków, ale 17 bajtów. Po odwróceniu bajtów para C5 82 zamienia się w 82 C5, czyli w ciąg, który nie jest już literą “ł”:
s = "kobyłamamałybok"
b = s.encode("utf-8")
print(len(s), len(b)) # 15 17
print(s == s[::-1]) # True
print(b == b[::-1]) # False

Druga pułapka: rozłożone litery w NFD
Ta sama litera z kreską może być zapisana na dwa sposoby. W formie NFC “ć” to jeden znak. W formie NFD to dwa znaki: “c” i osobny znak kreski. Unicode dopuszcza obie formy jako równoważne, więc program może dostać tekst zapisany w NFD, na przykład skopiowany z innego systemu.
W naszym teście odwrócenie słowa “aća” w formie NFD dało “áca”. Kreska przeskoczyła z “c” na “a”, a porównanie zwróciło False. Gorzej działa połączenie NFD z filtrem isalnum(). Filtr wyrzuca oderwane znaki kreski i ogonka, więc “ąa” zamienia się w “aa”. Funkcja przepuszcza wtedy napis, który od końca wcale nie brzmi tak samo.
Dlatego funkcja z poprzedniej sekcji zaczyna od unicodedata.normalize("NFC", tekst), opisanego w dokumentacji modułu unicodedata. Litera “ł” nie ma rozkładu na dwie części, więc “Kobyła ma mały bok” przechodzi test nawet w NFD. Więcej o kodowaniu polskich znaków piszemy w tekście o UTF-8 w Pythonie 3.15 i przy tabeli kodów ASCII.
Palindrom w C++
Porównanie bajtów i porównanie znaków
W C++ najprościej użyć funkcji std::equal, która porównuje pierwszą połowę napisu z jego końcem czytanym wstecz. Typ std::string przechowuje jednak bajty. Tekst w UTF-8 trafia do niego jako ciąg bajtów, więc z polskimi literami dzieje się to samo co w Pythonie przy encode(). Druga funkcja poniżej najpierw zamienia UTF-8 na kody znaków w std::u32string, a dopiero potem porównuje:
#include <algorithm>
#include <iostream>
#include <string>
// Wersja 1: porównanie bajt po bajcie (działa dla tekstu ASCII)
bool czy_palindrom(const std::string& s) {
return std::equal(s.begin(), s.begin() + s.size() / 2, s.rbegin());
}
// Zamiana UTF-8 na kody znaków (bez walidacji, dla poprawnego UTF-8)
std::u32string utf8_na_u32(const std::string& s) {
std::u32string wynik;
for (std::size_t i = 0; i < s.size();) {
unsigned char c = s[i];
char32_t kod;
int dlugosc;
if (c < 0x80) { kod = c; dlugosc = 1; }
else if (c < 0xE0) { kod = c & 0x1F; dlugosc = 2; }
else if (c < 0xF0) { kod = c & 0x0F; dlugosc = 3; }
else { kod = c & 0x07; dlugosc = 4; }
for (int k = 1; k < dlugosc; ++k)
kod = (kod << 6) | (static_cast<unsigned char>(s[i + k]) & 0x3F);
wynik.push_back(kod);
i += dlugosc;
}
return wynik;
}
// Wersja 2: porównanie znaków, a nie bajtów
bool czy_palindrom_utf8(const std::string& s) {
std::u32string z = utf8_na_u32(s);
return std::equal(z.begin(), z.begin() + z.size() / 2, z.rbegin());
}
int main() {
std::string kajak = "kajak";
std::string kobyla = "kobyłamamałybok";
std::cout << std::boolalpha;
std::cout << "bajty: " << kobyla.size() << '\n';
std::cout << "kajak: " << czy_palindrom(kajak) << '\n';
std::cout << "kobyla (bajty): " << czy_palindrom(kobyla) << '\n';
std::cout << "kobyla (znaki): " << czy_palindrom_utf8(kobyla) << '\n';
std::cout << "znaki: " << utf8_na_u32(kobyla).size() << '\n';
}
Wynik w Compiler Explorer
Program uruchomiliśmy w Compiler Explorer na kompilatorze x86-64 gcc 15.2 z opcją -std=c++20. Wynik:
bajty: 17
kajak: true
kobyla (bajty): false
kobyla (znaki): true
znaki: 15
Wersja 1 wystarcza dla tekstu ASCII, na przykład w zadaniach z olimpiady, gdzie dane zawierają tylko małe litery a–z. Przy oczyszczonym napisie z polskimi literami potrzebna jest wersja 2. Usuwanie spacji i interpunkcji oraz normalizację NFC trzeba w C++ dopisać osobno. Ujednolicenie wielkości liter dla “Ł” czy “Ż” wymaga w C++ osobnej tablicy zamian albo biblioteki do Unicode, takiej jak ICU. Funkcja std::tolower z nagłówka <cctype> zamienia pojedyncze znaki typu char, a polska litera w UTF-8 zajmuje dwa bajty. Więcej kodu w C++ z tej serii znajdziesz przy algorytmie Euklidesa.
Najdłuższy palindrom w tekście
Najdłuższy fragment czytany tak samo w obie strony znajdzie metoda rozszerzania od środka. Każdy taki fragment ma środek: jedną literę albo parę takich samych liter. Program bierze kolejno każdy możliwy środek i rozszerza się w obie strony, dopóki litery po lewej i prawej są równe:
def najdluzszy_palindrom(s):
if not s:
return ""
start, koniec = 0, 0
for srodek in range(len(s)):
for l, p in ((srodek, srodek), (srodek, srodek + 1)):
while l >= 0 and p < len(s) and s[l] == s[p]:
l -= 1
p += 1
if p - l - 1 > koniec - start:
start, koniec = l + 1, p
return s[start:koniec]
print(najdluzszy_palindrom("babad")) # bab
print(najdluzszy_palindrom("cbbd")) # bb
print(najdluzszy_palindrom("programowanie w kajaku")) # kajak
Dla “abacdc” funkcja zwraca “aba”, choć “cdc” jest tak samo długie. Przy remisie wygrywa fragment, który pojawił się wcześniej. Metoda ma złożoność O(n²) i stałą pamięć, co w zupełności wystarcza na zadania szkolne i rekrutacyjne. Dla bardzo długich tekstów istnieje algorytm Manachera, który rozwiązuje to samo zadanie w czasie liniowym, kosztem trudniejszego kodu.
Najczęstsze pytania
Czy pojedyncza litera jest palindromem?
Tak. Pojedyncza litera czytana od końca jest tą samą literą, a każda z funkcji powyżej zwraca dla niej True. Pusty napis też daje True. Jeśli program nie powinien akceptować pustego tekstu, sprawdź to osobnym warunkiem przed wywołaniem funkcji.
Jak sprawdzić palindrom rekurencyjnie?
Funkcja porównuje pierwszą i ostatnią literę, a potem wywołuje samą siebie dla środka napisu:
def czy_palindrom_rek(s):
if len(s) < 2:
return True
if s[0] != s[-1]:
return False
return czy_palindrom_rek(s[1:-1])
Dla “kajak” i “potop” zwraca True, dla “kot” zwraca False. To dobre ćwiczenie z rekurencji, ale w praktyce wersja z pętlą jest lepsza, bo każde wywołanie kopiuje wycinek napisu. Więcej przykładów takich funkcji znajdziesz w tekście o algorytmach rekurencyjnych.
Jak sprawdzić palindrom w JavaScripcie?
Ta wersja działa w Node.js 22 i w przeglądarce. Rozbija tekst na znaki operatorem ..., a filtr \p{L}\p{N} przepuszcza litery i cyfry, także polskie:
function czyPalindrom(tekst) {
const znaki = [...tekst.normalize("NFC").toLocaleLowerCase("pl")]
.filter(z => /[\p{L}\p{N}]/u.test(z));
return znaki.join("") === znaki.reverse().join("");
}
console.log(czyPalindrom("Kobyła ma mały bok.")); // true
console.log(czyPalindrom("Zagwiżdż i w gaz")); // true
console.log(czyPalindrom("Elf układa kufle.")); // false
Czym palindrom różni się od anagramu?
Palindrom czyta się tak samo od początku i od końca, jak “kajak”. Anagram powstaje z przestawienia liter innego wyrazu, jak “kot” i “tok”. Oba zadania często spotkasz obok siebie w zbiorach ćwiczeń z napisów, podobnie jak szyfr Cezara.
Podsumowanie
Palindrom to wyraz, zdanie, liczba albo data, które czytane od końca dają to samo: kajak, “Kobyła ma mały bok”, 12321, 22.02.2022. W zdaniach pomija się spacje, interpunkcję i wielkość liter. W Pythonie do jednego wyrazu wystarczy s == s[::-1]. Do zdań potrzebna jest normalizacja NFC, casefold() i filtr isalnum(). Polskie litery psują wynik wtedy, gdy program odwraca bajty UTF-8 zamiast znaków. Dotyczy to też std::string w C++, gdzie przed porównaniem trzeba zamienić tekst na std::u32string. Najbliższa data-palindrom to 3 lutego 2030 roku. Najdłuższy taki fragment w tekście znajdziesz metodą rozszerzania od środka w czasie O(n²). Zanim uznasz zdanie z internetu za taki przykład, sprawdź je kodem, bo “Elf układa kufle” testu nie przechodzi.
Newsletter · DevstockAcademy & Kodożercy
Bądź na bieżąco ze światem IT, AI i automatyzacji
Co wtorek: newsy z branży, praktyczne tipy i narzędzia które warto znać. Zero spamu.



