Работа со строками: решение популярных задач на Python
Основные приёмы работы со строками в Python
Как проверить, является ли строка палиндромом?
Самый эффективный способ — сравнение строки с её обратной копией с помощью среза. Метод не требует дополнительных циклов и работает за O(n).
def is_palindrome(s):
return s == s[::-1]задачи на строки python (задачи на строки на python)
>>> is_palindrome('radar')
True
>>> is_palindrome('hello')
False
Вариант с циклом — проверка с двух сторон без создания копии, полезно при ограничениях по памяти.
def is_palindrome_loop(s):
left, right = 0, len(s)-1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
Вариант с рекурсией — коротко, но ограничен глубиной стека.
def is_palindrome_rec(s):
if len(s) <= 1:
return True
if s[0] != s[-1]:
return False
return is_palindrome_rec(s[1:-1])
Типичные ошибки — игнорирование регистра и знаков пунктуации. Для корректной проверки следует предварительно очистить строку: s = ''.join(c.lower() for c in s if c.isalnum()). Если этого не сделать, строка 'A man, a plan, a canal: Panama' не будет распознана как палиндром.
Как подсчитать количество каждого символа в строке?
Использование класса Counter из модуля collections даёт готовое решение с минимальным кодом.
from collections import Counter
cnt = Counter('hello world')
>>> cnt
Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
Ручной подсчёт через словарь — простой и понятный, не требует импортов.
def count_chars(s):
d = {}
for ch in s:
d[ch] = d.get(ch, 0) + 1
return d
Использование defaultdict(int) — удобно, если не хотите писать условие.
from collections import defaultdict
d = defaultdict(int)
for ch in s:
d[ch] += 1
Проблемы возникают при работе с большими строками: Counter и словарь потребляют много памяти. Для потоковой обработки строки можно использовать массив фиксированного размера, если алфавит известен. Кроме того, при анализе текста на естественном языке часто требуется предварительная нормализация (удаление знаков, приведение к нижнему регистру).
Как удалить дублирующиеся символы, сохранив порядок их первого появления?
Класс OrderedDict из collections гарантирует порядок вставки и позволяет удалить дубликаты.
from collections import OrderedDict
def remove_duplicates(s):
return ''.join(OrderedDict.fromkeys(s))
>>> remove_duplicates('balloon')
'balon'
Использование set + list — менее элегантно, но работает.
def remove_duplicates_set(s):
seen = set()
result = []
for ch in s:
if ch not in seen:
seen.add(ch)
result.append(ch)
return ''.join(result)
Способ через enumerate — можно оставить только первое вхождение, но сложнее.
Ошибка — использование обычного set без сохранения порядка: ''.join(set(s)) вернёт символы в произвольном порядке. В Python 3.7+ обычный dict сохраняет порядок, но для явной гарантии лучше использовать OrderedDict. При работе с большими строками OrderedDict может занимать больше памяти, чем set+list.
Как проверить, являются ли две строки анаграммами?
Самый короткий способ — сортировка обеих строк и сравнение.
def are_anagrams(s1, s2):
return sorted(s1) == sorted(s2)
>>> are_anagrams('listen', 'silent')
True
>>> are_anagrams('hello', 'world')
False
Через Counter — более эффективно для больших строк (O(n) вместо O(n log n)).
from collections import Counter
def are_anagrams_counter(s1, s2):
return Counter(s1) == Counter(s2)
Ручной подсчёт — подходит для случаев, когда нужно контролировать каждую деталь.
def are_anagrams_manual(s1, s2):
if len(s1) != len(s2):
return False
counts = {}
for ch in s1:
counts[ch] = counts.get(ch, 0) + 1
for ch in s2:
if ch not in counts:
return False
counts[ch] -= 1
if counts[ch] == 0:
del counts[ch]
return True
Проблемы — учёт регистра и пробелов. Обычно строки приводят к нижнему регистру и удаляют пробелы. Сортировка может дать непредсказуемый результат для строк с разными регистрами. Counter одинаково обрабатывает любые символы, но создаёт два объекта, что нагружает память.
Как заменить все вхождения подстроки в строке?
Метод replace выполняет замену за один вызов.
text = 'Python is great. I love Python.'
text.replace('Python', 'JavaScript')
'JavaScript is great. I love JavaScript.'
Регулярные выражения (re.sub) — если нужна замена по шаблону, например, замена всех цифр на пустую строку.
import re
re.sub(r'\d+', '', 'abc123def456')
'abcdef'
Цикл с find — для пошагового контроля замен, например, подсчёт количества замен.
def custom_replace(text, old, new):
result = []
i = 0
cnt = 0
while i < len(text):
if text.startswith(old, i):
result.append(new)
i += len(old)
cnt += 1
else:
result.append(text[i])
i += 1
return ''.join(result), cnt
Ошибка — замена только первого вхождения. По умолчанию replace заменяет все, но третий параметр count ограничивает количество. При использовании re.sub без флага re.DOTALL не затрагиваются переводы строк. Спецсимволы в old (например, точка) в re.sub интерпретируются как метасимволы, требуется экранирование.
Расширенные примеры и неочевидные случаи
Палиндром с игнорированием пунктуации и регистра
import re
def is_palindrome_clean(s):
s = re.sub(r'[^a-zA-Z0-9]', '', s).lower()
return s == s[::-1]
# Тест
print(is_palindrome_clean('A man, a plan, a canal: Panama'))
True
Подсчёт символов в файле (построчно) — экономный по памяти способ с использованием defaultdict.
from collections import defaultdict
total = defaultdict(int)
with open('text.txt', 'r', encoding='utf-8') as f:
for line in f:
for ch in line:
total[ch] += 1
print(dict(total))
{'H': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1, '\n': 1}
Удаление дубликатов с помощью dict (Python 3.7+)
def remove_duplicates_dict(s):
return ''.join(dict.fromkeys(s))
print(remove_duplicates_dict('banana'))
'ban'
Анаграммы с учётом частоты слов — не только символы, но и слова.
from collections import Counter
def are_anagrams_phrase(s1, s2):
# удаляем знаки, приводим к нижнему, разбиваем на слова
words1 = re.findall(r'\b\w+\b', s1.lower())
words2 = re.findall(r'\b\w+\b', s2.lower())
return Counter(words1) == Counter(words2)
print(are_anagrams_phrase('a decimal point', 'i am a dot in place'))
True
Замена с использованием регулярного выражения с именнованной группой
import re
text = 'Дата: 2023-01-15, событие: встреча'
# замена формата даты с YYYY-MM-DD на DD.MM.YYYY
result = re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3.\2.\1', text)
print(result)
Дата: 15.01.2023, событие: встреча