← Назад к вопросам
Подсчёт подстрок
1.0 Junior🔥 171 комментариев
#Python Core
Условие
Напишите функцию, которая считает количество вхождений подстроки в строку, включая перекрывающиеся вхождения.
Пример
count_substr("abababa", "aba") → 3 count_substr("hello", "l") → 2 count_substr("aaa", "aa") → 2
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Подсчёт подстрок
Эта задача заключается в подсчёте всех вхождений подстроки в строку, включая перекрывающиеся вхождения. Это важное отличие от встроенной функции count(), которая не считает перекрывающиеся вхождения.
Простое решение
def count_substr(string: str, substring: str) -> int:
"""
Считает количество вхождений подстроки в строку, включая перекрывающиеся.
Args:
string: Основная строка для поиска
substring: Подстрока для поиска
Returns:
Количество вхождений подстроки
Examples:
>>> count_substr("abababa", "aba")
3
>>> count_substr("hello", "l")
2
>>> count_substr("aaa", "aa")
2
"""
if not substring or not string:
return 0
# Перемещаемся по строке и считаем совпадения
count = 0
for i in range(len(string) - len(substring) + 1):
if string[i:i+len(substring)] == substring:
count += 1
return count
Сравнение с встроенной функцией
# Встроенная функция count() НЕ считает перекрывающиеся
print("abababa".count("aba")) # 2 (не считает перекрывающиеся)
print(count_substr("abababa", "aba")) # 3 (считает все)
print("hello".count("l")) # 2
print(count_substr("hello", "l")) # 2
print("aaa".count("aa")) # 1 (только две подряд)
print(count_substr("aaa", "aa")) # 2 (считает перекрывающиеся)
Решение с использованием find()
def count_substr_with_find(string: str, substring: str) -> int:
"""
Альтернативное решение, используя метод find().
"""
if not substring or not string:
return 0
count = 0
start = 0
while True:
# find() возвращает индекс первого совпадения или -1
pos = string.find(substring, start)
if pos == -1:
break
count += 1
# Двигаемся на 1 символ, чтобы учесть перекрытия
start = pos + 1
return count
Решение с использованием регулярных выражений
import re
def count_substr_regex(string: str, substring: str) -> int:
"""
Решение с использованием регулярных выражений.
"""
if not substring or not string:
return 0
# Экранируем специальные символы в подстроке
escaped = re.escape(substring)
# (?=...) — позитивная lookahead, позволяет считать перекрытия
pattern = f"(?={escaped})"
return len(re.findall(pattern, string))
Примеры использования
# Основные примеры
print(count_substr("abababa", "aba")) # 3
print(count_substr("hello", "l")) # 2
print(count_substr("aaa", "aa")) # 2
# Дополнительные примеры
print(count_substr("abcabcabc", "abc")) # 3
print(count_substr("aaaa", "aa")) # 3 (позиции 0,1,2)
print(count_substr("hello world", "o")) # 2
print(count_substr("test", "xyz")) # 0 (нет совпадений)
print(count_substr("", "a")) # 0 (пустая строка)
print(count_substr("hello", "")) # 0 (пустая подстрока)
Оптимизированное решение
def count_substr_optimized(string: str, substring: str) -> int:
"""
Оптимизированная версия с ранним выходом.
"""
if not substring or not string or len(substring) > len(string):
return 0
sub_len = len(substring)
str_len = len(string)
count = 0
for i in range(str_len - sub_len + 1):
# Используем встроенное сравнение (оптимизировано на C уровне)
if string[i:i+sub_len] == substring:
count += 1
return count
Решение для больших строк (KMP алгоритм)
def count_substr_kmp(string: str, substring: str) -> int:
"""
Решение с использованием KMP (Knuth-Morris-Pratt) алгоритма.
Более эффективно для больших строк и сложных паттернов.
"""
if not substring or not string or len(substring) > len(string):
return 0
# Построение таблицы префиксов
def build_lps(pattern):
m = len(pattern)
lps = [0] * m
length = 0
i = 1
while i < m:
if pattern[i] == pattern[length]:
length += 1
lps[i] = length
i += 1
else:
if length != 0:
length = lps[length - 1]
else:
lps[i] = 0
i += 1
return lps
n = len(string)
m = len(substring)
lps = build_lps(substring)
count = 0
i = 0 # индекс для string
j = 0 # индекс для substring
while i < n:
if substring[j] == string[i]:
i += 1
j += 1
if j == m:
count += 1
j = lps[j - 1] # Для перекрывающихся вхождений
elif i < n and substring[j] != string[i]:
if j != 0:
j = lps[j - 1]
else:
i += 1
return count
Тестирование
def test_count_substr():
# Примеры из задания
assert count_substr("abababa", "aba") == 3
assert count_substr("hello", "l") == 2
assert count_substr("aaa", "aa") == 2
# Граничные случаи
assert count_substr("", "a") == 0
assert count_substr("hello", "") == 0
assert count_substr("", "") == 0
# Нет совпадений
assert count_substr("hello", "xyz") == 0
# Одно совпадение
assert count_substr("abc", "abc") == 1
assert count_substr("hello", "h") == 1
# Все символы
assert count_substr("aaaa", "a") == 4
# Перекрывающиеся
assert count_substr("aaaa", "aa") == 3
assert count_substr("abcabcabc", "abc") == 3
# Длинная подстрока
assert count_substr("hello", "hello") == 1
assert count_substr("hello", "helloworld") == 0
print("Все тесты пройдены!")
test_count_substr()
Сравнение производительности
import time
def benchmark():
# Большая строка для тестирования
big_string = "abab" * 10000
start = time.time()
result1 = count_substr(big_string, "aba")
time1 = time.time() - start
start = time.time()
result2 = count_substr_with_find(big_string, "aba")
time2 = time.time() - start
start = time.time()
result3 = count_substr_kmp(big_string, "aba")
time3 = time.time() - start
print(f"Простое решение: {time1:.6f}s, результат: {result1}")
print(f"С find(): {time2:.6f}s, результат: {result2}")
print(f"KMP: {time3:.6f}s, результат: {result3}")
benchmark()
Анализ сложности
Простое решение:
- Временная сложность: O(n·m), где n — длина строки, m — длина подстроки
- Пространственная сложность: O(1)
KMP алгоритм:
- Временная сложность: O(n + m)
- Пространственная сложность: O(m)
Для большинства практических случаев простое решение достаточно эффективно. KMP рекомендуется для очень больших строк и сложных паттернов.