Add: add optimized solution for second day tasks

This commit is contained in:
Kirill Samoylenkov 2025-12-02 13:39:34 +05:00
parent 1cc4e213d7
commit ff470788b0

View file

@ -0,0 +1,164 @@
"""
Оптимизированное решение для задачи дня 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()