164 lines
6.6 KiB
Python
164 lines
6.6 KiB
Python
"""
|
||
Оптимизированное решение для задачи дня 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()
|