advent-of-code-2025/python/src/2-day/optimized_solution.py

164 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Оптимизированное решение для задачи дня 2: Подарочный магазин
Основная идея оптимизации:
Вместо перебора всех чисел в диапазонах (что может быть очень медленно для больших диапазонов),
мы генерируем только числа нужной формы и проверяем, попадают ли они в заданные диапазоны.
Для части 1: числа вида S+S (дважды повторяющаяся последовательность)
Для части 2: числа вида S+S+...+S (k раз, k >= 2)
"""
from typing import List, Tuple, Set
def parse_ranges(input_str: str) -> List[Tuple[int, int]]:
"""Парсит строку с диапазонами вида 'start-end,start2-end2'"""
ranges = []
for part in input_str.strip().split(","):
if part: # пропускаем пустые части
start, end = map(int, part.split("-"))
ranges.append((start, end))
return ranges
def is_in_ranges(number: int, ranges: List[Tuple[int, int]]) -> bool:
"""Проверяет, находится ли число в одном из диапазонов"""
# Сортируем диапазоны для оптимизации (можно использовать бинарный поиск)
# Но для небольшого количества диапазонов линейный поиск тоже эффективен
for start, end in ranges:
if start <= number <= end:
return True
return False
def generate_repeated_numbers_part1(max_len: int = 10) -> Set[int]:
"""
Генерирует все числа вида S+S для части 1
Аргументы:
max_len: максимальная длина числа (по умолчанию 10, так как в input.txt
максимальная длина числа около 10-11 цифр)
Возвращает:
Множество чисел вида S+S (например, 55, 6464, 123123)
"""
result = set()
# Длина повторяющейся последовательности S
for s_len in range(1, max_len // 2 + 1):
# Генерируем все возможные S от 10^(s_len-1) до 10^s_len - 1
start = 10 ** (s_len - 1) if s_len > 1 else 0
end = 10**s_len
for s in range(start, end):
# Пропускаем числа, начинающиеся с 0 (кроме самого 0)
if s == 0 or str(s)[0] != "0":
repeated = int(str(s) * 2)
if len(str(repeated)) <= max_len:
result.add(repeated)
return result
def generate_repeated_numbers_part2(max_len: int = 10) -> Set[int]:
"""
Генерирует все числа вида S*k (k >= 2) для части 2
Аргументы:
max_len: максимальная длина числа
Возвращает:
Множество чисел вида S*k (например, 12341234, 123123123, 1111111)
"""
result = set()
# Длина повторяющейся последовательности S
for s_len in range(1, max_len // 2 + 1):
# Генерируем все возможные S
start = 10 ** (s_len - 1) if s_len > 1 else 0
end = 10**s_len
for s in range(start, end):
# Пропускаем числа, начинающиеся с 0 (кроме самого 0)
if s == 0 or str(s)[0] != "0":
s_str = str(s)
# Количество повторений k (минимум 2)
for k in range(2, max_len // s_len + 1):
repeated_str = s_str * k
if len(repeated_str) <= max_len:
repeated = int(repeated_str)
result.add(repeated)
return result
def solve_part1(ranges: List[Tuple[int, int]]) -> int:
"""
Решение части 1: сумма чисел вида S+S в диапазонах
Оптимизация:
- Генерируем все возможные числа нужной формы заранее
- Проверяем только их принадлежность диапазонам
- Используем множество для быстрого поиска (хотя в данном случае это не критично)
"""
# Определяем максимальную длину числа в диапазонах
max_number = max(end for _, end in ranges)
max_len = len(str(max_number))
# Генерируем все числа вида S+S
candidates = generate_repeated_numbers_part1(max_len)
# Суммируем только те, что попадают в диапазоны
total = 0
for number in candidates:
if is_in_ranges(number, ranges):
total += number
return total
def solve_part2(ranges: List[Tuple[int, int]]) -> int:
"""
Решение части 2: сумма чисел вида S*k (k >= 2) в диапазонах
Оптимизация:
- Генерируем все возможные числа нужной формы заранее
- Проверяем только их принадлежность диапазонам
- Избегаем перебора миллионов чисел в диапазонах
"""
# Определяем максимальную длину числа в диапазонах
max_number = max(end for _, end in ranges)
max_len = len(str(max_number))
# Генерируем все числа вида S*k (k >= 2)
candidates = generate_repeated_numbers_part2(max_len)
# Суммируем только те, что попадают в диапазоны
total = 0
for number in candidates:
if is_in_ranges(number, ranges):
total += number
return total
def main():
# Чтение входных данных
with open("./src/2-day/input.txt", "r") as file:
input_str = file.read().strip()
ranges = parse_ranges(input_str)
# Решение части 1
result1 = solve_part1(ranges)
print(f"Часть 1 (сумма чисел вида S+S): {result1}")
# Решение части 2
result2 = solve_part2(ranges)
print(f"Часть 2 (сумма чисел вида S*k, k>=2): {result2}")
if __name__ == "__main__":
main()