← Назад к вопросам

Проверка палиндрома (строка)

1.0 Junior🔥 241 комментариев
#Python Core

Условие

Напишите функцию, которая проверяет, является ли строка палиндромом. Игнорируйте регистр и небуквенные символы.

Пример

is_palindrome("А роза упала на лапу Азора") → True is_palindrome("Was it a car or a cat I saw?") → True is_palindrome("hello") → False

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Проверка палиндрома: полный разбор

Палиндром — это строка, которая читается одинаково в обе стороны. При проверке игнорируются регистр и небуквенные символы.

Решение 1: Классический подход (Simple)

def is_palindrome_simple(s: str) -> bool:
    """
    Проверяет, является ли строка палиндромом.
    Игнорирует регистр и небуквенные символы.
    
    Подход: очистка строки, затем сравнение с обратной версией.
    """
    # Преобразуем в нижний регистр и оставляем только буквы
    cleaned = ''.join(char.lower() for char in s if char.isalpha())
    
    # Сравниваем с обратной версией
    return cleaned == cleaned[::-1]

# Примеры
print(is_palindrome_simple("А роза упала на лапу Азора"))  # True
print(is_palindrome_simple("Was it a car or a cat I saw?"))  # True
print(is_palindrome_simple("hello"))  # False
print(is_palindrome_simple("racecar"))  # True
print(is_palindrome_simple("Madam"))  # True
print(is_palindrome_simple("12321"))  # True

Решение 2: Двухуказательный подход (Two Pointers)

Более эффективно для больших строк, так как не создаём новую строку.

def is_palindrome_two_pointers(s: str) -> bool:
    """
    Проверяет палиндром с помощью двух указателей.
    
    Сложность: O(n) время, O(1) память
    
    Подход:
    - Два указателя: слева и справа
    - Двигаются навстречу друг другу
    - Пропускаем небуквенные символы
    """
    left = 0
    right = len(s) - 1
    
    while left < right:
        # Пропускаем небуквенные символы слева
        while left < right and not s[left].isalpha():
            left += 1
        
        # Пропускаем небуквенные символы справа
        while left < right and not s[right].isalpha():
            right -= 1
        
        # Сравниваем символы (игнорируя регистр)
        if s[left].lower() != s[right].lower():
            return False
        
        left += 1
        right -= 1
    
    return True

# Примеры
print(is_palindrome_two_pointers("А роза упала на лапу Азора"))  # True
print(is_palindrome_two_pointers("Was it a car or a cat I saw?"))  # True
print(is_palindrome_two_pointers("hello"))  # False
print(is_palindrome_two_pointers("A man, a plan, a canal: Panama"))  # True

Решение 3: С регулярными выражениями

import re

def is_palindrome_regex(s: str) -> bool:
    """
    Проверяет палиндром с помощью регулярных выражений.
    
    Подход:
    - Используем regex для очистки строки
    - Оставляем только буквы и цифры
    """
    # Оставляем только буквы и цифры, преобразуем в нижний регистр
    cleaned = re.sub(r'[^a-z0-9]', '', s.lower())
    
    # Сравниваем с обратной версией
    return cleaned == cleaned[::-1]

# Примеры
print(is_palindrome_regex("А роза упала на лапу Азора"))  # True (для латиницы)
print(is_palindrome_regex("Was it a car or a cat I saw?"))  # True
print(is_palindrome_regex("hello"))  # False
print(is_palindrome_regex("Race car!"))  # True

Решение 4: С типизацией и документацией

def is_palindrome(text: str) -> bool:
    """
    Проверяет, является ли строка палиндромом.
    
    Игнорирует регистр и небуквенные символы.
    
    Args:
        text: Строка для проверки
    
    Returns:
        True, если строка палиндром, False иначе
    
    Raises:
        TypeError: Если text не строка
    
    Examples:
        >>> is_palindrome("А роза упала на лапу Азора")
        True
        >>> is_palindrome("Was it a car or a cat I saw?")
        True
        >>> is_palindrome("hello")
        False
        >>> is_palindrome("Madam, I'm Adam")
        True
    """
    if not isinstance(text, str):
        raise TypeError(f"Expected str, got {type(text).__name__}")
    
    # Очищаем строку: нижний регистр, только буквы
    cleaned = ''.join(char.lower() for char in text if char.isalpha())
    
    # Проверяем, равна ли строка своему обратному значению
    return cleaned == cleaned[::-1]

# Тестирование
print(is_palindrome("А роза упала на лапу Азора"))  # True
print(is_palindrome("Was it a car or a cat I saw?"))  # True
print(is_palindrome("hello"))  # False

try:
    is_palindrome(12345)  # TypeError
except TypeError as e:
    print(f"Ошибка: {e}")

Решение 5: Рекурсивный подход

def is_palindrome_recursive(s: str) -> bool:
    """
    Проверяет палиндром рекурсивно.
    
    Примечание: менее эффективно, но демонстрирует рекурсию.
    """
    # Очищаем строку один раз в начале
    def clean(text):
        return ''.join(char.lower() for char in text if char.isalpha())
    
    cleaned = clean(s)
    
    def check(left: int, right: int) -> bool:
        # Базовый случай: указатели встретились
        if left >= right:
            return True
        
        # Рекурсивный случай: проверяем текущую пару и двигаемся дальше
        if cleaned[left] != cleaned[right]:
            return False
        
        return check(left + 1, right - 1)
    
    return check(0, len(cleaned) - 1)

# Примеры
print(is_palindrome_recursive("А роза упала на лапу Азора"))  # True
print(is_palindrome_recursive("Was it a car or a cat I saw?"))  # True
print(is_palindrome_recursive("hello"))  # False

Решение 6: Генераторное выражение (Pythonic)

def is_palindrome_pythonic(s: str) -> bool:
    """
    Питоничный подход с использованием генератора и all().
    """
    # Преобразуем в итератор букв
    chars = [char.lower() for char in s if char.isalpha()]
    
    # Проверяем, совпадают ли символы с начала и конца
    return all(
        chars[i] == chars[-(i+1)]
        for i in range(len(chars) // 2)
    )

# Примеры
print(is_palindrome_pythonic("А роза упала на лапу Азора"))  # True
print(is_palindrome_pythonic("Was it a car or a cat I saw?"))  # True
print(is_palindrome_pythonic("hello"))  # False

Сравнение производительности

import time

def benchmark_palindrome():
    """
    Сравнивает производительность разных подходов.
    """
    test_string = "А роза упала на лапу Азора" * 1000  # Большая строка
    
    # Метод 1: Простой
    start = time.time()
    for _ in range(1000):
        is_palindrome_simple(test_string)
    time1 = time.time() - start
    print(f"Простой подход: {time1:.4f}с")
    
    # Метод 2: Двухуказательный
    start = time.time()
    for _ in range(1000):
        is_palindrome_two_pointers(test_string)
    time2 = time.time() - start
    print(f"Двухуказательный: {time2:.4f}с")
    
    # Метод 3: Pythonic
    start = time.time()
    for _ in range(1000):
        is_palindrome_pythonic(test_string)
    time3 = time.time() - start
    print(f"Pythonic: {time3:.4f}с")

# benchmark_palindrome()
# Результаты примерно одинаковые для этого размера

Тестирование

def test_is_palindrome():
    """
    Comprehensive тестирование функции.
    """
    # Палиндромы
    assert is_palindrome("А роза упала на лапу Азора") == True
    assert is_palindrome("Was it a car or a cat I saw?") == True
    assert is_palindrome("A man, a plan, a canal: Panama") == True
    assert is_palindrome("Madam") == True
    assert is_palindrome("racecar") == True
    assert is_palindrome("a") == True
    assert is_palindrome("") == True  # Пустая строка
    assert is_palindrome("12321") == True
    assert is_palindrome("!@#!@#") == True  # Только спецсимволы
    
    # Не палиндромы
    assert is_palindrome("hello") == False
    assert is_palindrome("world") == False
    assert is_palindrome("12345") == False
    assert is_palindrome("ab") == False
    
    # Регистр
    assert is_palindrome("RaceCar") == True
    assert is_palindrome("MADAM") == True
    
    # С спецсимволами
    assert is_palindrome("No 'x' in Nixon") == True
    assert is_palindrome("race-car") == True
    
    print("✓ Все тесты пройдены!")

test_is_palindrome()

Сложность алгоритмов

ПодходВремяПамятьПримечание
ПростойO(n)O(n)Создаёт новую строку
ДвухуказательныйO(n)O(1)Рекомендуется (optimized)
С regexO(n)O(n)Медленнее, но удобно
РекурсивныйO(n)O(n)Стек вызовов
PythonicO(n)O(n)Читаемо, но неэффективно

Рекомендуемое решение для Production

def is_palindrome(text: str) -> bool:
    """
    Проверяет, является ли строка палиндромом.
    Игнорирует регистр и небуквенные символы.
    
    O(n) время, O(n) память
    """
    if not isinstance(text, str):
        raise TypeError(f"Expected str, got {type(text).__name__}")
    
    # Очищаем строку один раз
    cleaned = ''.join(char.lower() for char in text if char.isalpha())
    
    # Сравниваем с обратной версией
    return cleaned == cleaned[::-1]

# Баланс:
# - Просто и понятно
# - Эффективно (O(n))
# - Питонично (встроенные методы)
# - Легко поддерживать

Вариации задачи

# Включить цифры
def is_alphanumeric_palindrome(s: str) -> bool:
    cleaned = ''.join(char.lower() for char in s if char.isalnum())
    return cleaned == cleaned[::-1]

print(is_alphanumeric_palindrome("A1b2B1a"))  # True

# Включить пробелы
def is_palindrome_with_spaces(s: str) -> bool:
    cleaned = ''.join(char.lower() for char in s if char != ' ')
    return cleaned == cleaned[::-1]

print(is_palindrome_with_spaces("race car"))  # True

# Проверить слова
def is_word_palindrome(s: str) -> bool:
    words = s.lower().split()
    return words == words[::-1]

print(is_word_palindrome("race car race"))  # True