├── bonus1 ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── d_workshops.py ├── a_polynomial_hash.py ├── f_anagram_grouping.py ├── e_substrings.py ├── b_crash_me.py ├── g_competition.py ├── h_weird_comparison.py ├── c_prefix_hashes.py ├── l_mnogogosha.py ├── j_sum_of_four.py ├── i_common_subarray.py └── k_nearest_stop.py ├── bonus2 ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── m_sieve_up.py ├── i_different_search_trees.py ├── l_sieve_down.py ├── f_max_depth.py ├── a_lamps.py ├── d_tree_twins.py ├── k_show_range.py ├── h_num_paths.py ├── j_add_node.py ├── g_max_path.py ├── b_balanced_tree.py ├── c_anagram_tree.py ├── n_tree_partition.py └── e_search_tree.py ├── shbr1 ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── a_steps.py ├── b_canonical_path.py ├── d_time_difference.py ├── e_break_palindrome.py └── c_buy_sell.py ├── shbr2 ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── d_majority.py ├── b_multicolor_sticks.py ├── a_repeating_number.py ├── e_deleting_numbers.py └── c_replacing_words.py ├── shbr3 ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── a_count_positive.py ├── d_cows_in_stalls.py ├── b_boomers_and_zoomers.py ├── e_brewing_potions.py └── c_festive_lights.py ├── shbr4 ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── b_guests.py ├── a_groups_and_rooms.py ├── c_socks.py ├── e_filepath.py └── d_subtree_size.py ├── shbr5 ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── c_mountains.py ├── d_delete_string.py ├── a_bulls_and_cows.py ├── b_largest_substring.py └── e_pitcraft.py ├── sprint1 ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── a_function_values.py ├── i_power_of_four.py ├── f_palindrome.py ├── b_even_odd.py ├── l_extra_letter.py ├── g_working_from_home.py ├── d_weather.py ├── e_longest_word.py ├── h_binary.py ├── j_factorization.py ├── lesson_average.py ├── final_sleight_of_hand.py ├── lesson_zipper.py ├── k_list_form.py ├── final_nearest_zero.py ├── lesson_twosum.py └── c_neighbors.py ├── sprint2 ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── k_rec_fibonacci.py ├── l_mod_fibonacci.py ├── a_monitoring.py ├── b_todo.py ├── h_bracket_sequence.py ├── final_calc.py ├── c_tedious_work.py ├── d_caring_mother.py ├── f_stack_max.py ├── e_reversed.py ├── j_list_queue.py ├── final_deque.py └── i_limited_queue.py ├── sprint3 ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── img │ ├── broken_search.png │ └── broken_search.psd ├── f_triangle.py ├── g_wardrobe.py ├── a_bracket_gen.py ├── h_large_number.py ├── i_conference_fans.py ├── d_cookies.py ├── c_subsequence.py ├── j_bubble.py ├── p_partial_sort.py ├── k_merge_sort.py ├── e_houses.py ├── b_combinations.py ├── n_flowerbeds.py ├── rec_call_tree.py ├── m_golden_mean.py ├── o_index_difference.py ├── l_two_bicycles.py ├── final_broken_search.py └── final_efficient_quicksort.py ├── yaintern ├── __init__.py ├── tests │ ├── __init__.py │ └── test_problems.py ├── b_even_fence.py ├── a_usb.py ├── c_repo_inheritance.py ├── e_priority_admission.py └── d_digs_and_tiles.py ├── .python-version ├── .gitignore ├── LICENSE ├── pyproject.toml └── uv.lock /bonus1/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bonus2/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shbr1/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shbr2/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shbr3/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shbr4/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shbr5/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shbr1/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shbr2/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shbr3/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shbr4/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shbr5/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sprint1/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sprint2/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sprint3/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /yaintern/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.13 2 | -------------------------------------------------------------------------------- /bonus1/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bonus2/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sprint1/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sprint2/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sprint3/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /yaintern/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .pytest_cache 3 | .ruff_cache 4 | __pycache__ 5 | -------------------------------------------------------------------------------- /sprint3/img/broken_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monk-time/algorithms/HEAD/sprint3/img/broken_search.png -------------------------------------------------------------------------------- /sprint3/img/broken_search.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monk-time/algorithms/HEAD/sprint3/img/broken_search.psd -------------------------------------------------------------------------------- /sprint1/a_function_values.py: -------------------------------------------------------------------------------- 1 | def func(a, x, b, c): 2 | return a * x**2 + b * x + c 3 | 4 | 5 | if __name__ == '__main__': 6 | a, x, b, c = map(int, input().split()) 7 | print(func(a, x, b, c)) 8 | -------------------------------------------------------------------------------- /sprint1/i_power_of_four.py: -------------------------------------------------------------------------------- 1 | def is_power_of_four(n: int) -> bool: 2 | while n % 4 == 0: 3 | n //= 4 4 | return n == 1 5 | 6 | 7 | if __name__ == '__main__': 8 | n = int(input()) 9 | print(is_power_of_four(n)) 10 | -------------------------------------------------------------------------------- /bonus1/d_workshops.py: -------------------------------------------------------------------------------- 1 | def unique(a: list) -> list: 2 | return list(dict.fromkeys(a).keys()) 3 | 4 | 5 | if __name__ == '__main__': 6 | count = int(input()) 7 | a = [input() for _ in range(count)] 8 | print(*unique(a), sep='\n') 9 | -------------------------------------------------------------------------------- /sprint1/f_palindrome.py: -------------------------------------------------------------------------------- 1 | def is_palindrome(s: str) -> bool: 2 | alphanums = list(filter(str.isalnum, s.lower())) 3 | return alphanums == alphanums[::-1] 4 | 5 | 6 | if __name__ == '__main__': 7 | phrase = input() 8 | print(is_palindrome(phrase)) 9 | -------------------------------------------------------------------------------- /sprint1/b_even_odd.py: -------------------------------------------------------------------------------- 1 | def check_parity(a: int, b: int, c: int) -> str: 2 | same_parity = a % 2 == b % 2 == c % 2 3 | return ('FAIL', 'WIN')[same_parity] 4 | 5 | 6 | if __name__ == '__main__': 7 | a, b, c = map(int, input().split()) 8 | print(check_parity(a, b, c)) 9 | -------------------------------------------------------------------------------- /sprint2/k_rec_fibonacci.py: -------------------------------------------------------------------------------- 1 | from functools import cache 2 | 3 | 4 | @cache 5 | def fib(n: int) -> int: 6 | if n <= 1: 7 | return 1 8 | return fib(n - 1) + fib(n - 2) 9 | 10 | 11 | if __name__ == '__main__': 12 | n = int(input()) 13 | print(fib(n)) 14 | -------------------------------------------------------------------------------- /shbr2/d_majority.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | def majority(a: list[int]) -> int: 5 | return Counter(a).most_common(1)[0][0] 6 | 7 | 8 | if __name__ == '__main__': 9 | n = int(input()) 10 | a = [int(s) for s in input().split()] 11 | print(majority(a)) 12 | -------------------------------------------------------------------------------- /sprint1/l_extra_letter.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | def extra_letter(s: str, t: str) -> str: 5 | diff = Counter(t) - Counter(s) 6 | return diff.most_common(1)[0][0] 7 | 8 | 9 | if __name__ == '__main__': 10 | s = input() 11 | t = input() 12 | print(extra_letter(s, t)) 13 | -------------------------------------------------------------------------------- /sprint1/g_working_from_home.py: -------------------------------------------------------------------------------- 1 | def to_binary(n: int) -> str: 2 | if n == 0: 3 | return '0' 4 | result = '' 5 | while n: 6 | result += str(n % 2) 7 | n //= 2 8 | return result[::-1] 9 | 10 | 11 | if __name__ == '__main__': 12 | n = int(input()) 13 | print(to_binary(n)) 14 | -------------------------------------------------------------------------------- /bonus2/m_sieve_up.py: -------------------------------------------------------------------------------- 1 | def sift_up(heap: list[int], index: int) -> int: 2 | if index == 1: 3 | return index 4 | 5 | parent = index // 2 6 | if heap[parent] >= heap[index]: 7 | return index 8 | 9 | heap[parent], heap[index] = heap[index], heap[parent] 10 | return sift_up(heap, parent) 11 | -------------------------------------------------------------------------------- /sprint2/l_mod_fibonacci.py: -------------------------------------------------------------------------------- 1 | def fib_mod(n: int, k: int) -> int: 2 | prev = last = 1 3 | divisor = 10**k 4 | for _ in range(n - 1): 5 | prev, last = last, (prev + last) % divisor 6 | return last 7 | 8 | 9 | if __name__ == '__main__': 10 | n, k = map(int, input().split()) 11 | print(fib_mod(n, k)) 12 | -------------------------------------------------------------------------------- /bonus1/a_polynomial_hash.py: -------------------------------------------------------------------------------- 1 | from functools import reduce 2 | 3 | 4 | def polynomial_hash(a: int, m: int, s: str) -> int: 5 | return reduce(lambda acc, ch: (acc * a + ord(ch)) % m, s, 0) 6 | 7 | 8 | if __name__ == '__main__': 9 | a = int(input()) 10 | m = int(input()) 11 | s = input() 12 | print(polynomial_hash(a, m, s)) 13 | -------------------------------------------------------------------------------- /shbr5/c_mountains.py: -------------------------------------------------------------------------------- 1 | def find_peak(mountains: list[int]) -> int: 2 | prev, i = mountains[0], 1 3 | while prev <= mountains[i]: 4 | prev = mountains[i] 5 | i += 1 6 | return i 7 | 8 | 9 | if __name__ == '__main__': 10 | input() 11 | mountains = [int(s) for s in input().split()] 12 | print(find_peak(mountains)) 13 | -------------------------------------------------------------------------------- /shbr1/a_steps.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | 4 | def steps_iter(x: int) -> int: 5 | n, total = 0, 0 6 | while total <= x: 7 | n += 1 8 | total += n 9 | return n - 1 10 | 11 | 12 | def steps(x: int) -> int: 13 | return math.floor((math.sqrt(8 * x + 1) - 1) / 2) 14 | 15 | 16 | if __name__ == '__main__': 17 | n = int(input()) 18 | print(steps(n)) 19 | -------------------------------------------------------------------------------- /sprint1/d_weather.py: -------------------------------------------------------------------------------- 1 | def calc_chaos(temps: list[int]) -> int: 2 | n = len(temps) 3 | return sum( 4 | (i == 0 or temps[i] > temps[i - 1]) 5 | and (i == n - 1 or temps[i] > temps[i + 1]) 6 | for i in range(n) 7 | ) 8 | 9 | 10 | if __name__ == '__main__': 11 | _ = int(input()) 12 | temps = [int(s) for s in input().split()] 13 | print(calc_chaos(temps)) 14 | -------------------------------------------------------------------------------- /shbr2/b_multicolor_sticks.py: -------------------------------------------------------------------------------- 1 | import re 2 | from collections import defaultdict 3 | 4 | 5 | def count_sticks(s: str) -> int: 6 | sticks = defaultdict(set) 7 | for color, num in re.findall(r'\w\d', s): 8 | sticks[num].add(color) 9 | return sum(len(stick) == 3 for stick in sticks.values()) 10 | 11 | 12 | if __name__ == '__main__': 13 | s = input() 14 | print(count_sticks(s)) 15 | -------------------------------------------------------------------------------- /sprint3/f_triangle.py: -------------------------------------------------------------------------------- 1 | def max_perimeter(lens: list[int]) -> int | None: 2 | lens.sort(reverse=True) 3 | for i in range(len(lens) - 2): 4 | c, b, a = lens[i : i + 3] 5 | if c < a + b: 6 | return a + b + c 7 | return None 8 | 9 | 10 | if __name__ == '__main__': 11 | n = int(input()) 12 | lens = list(map(int, input().split())) 13 | print(max_perimeter(lens)) 14 | -------------------------------------------------------------------------------- /sprint3/g_wardrobe.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from collections.abc import Iterable 3 | 4 | 5 | def counting_sort(a: list[int]) -> Iterable[int]: 6 | for key, count in sorted(Counter(a).items()): 7 | for _ in range(count): 8 | yield key 9 | 10 | 11 | if __name__ == '__main__': 12 | _ = int(input()) 13 | a = list(map(int, input().split())) 14 | print(*counting_sort(a)) 15 | -------------------------------------------------------------------------------- /sprint1/e_longest_word.py: -------------------------------------------------------------------------------- 1 | def find_longest_word(sentence: str) -> str: 2 | longest_word = '' 3 | for word in sentence.split(' '): 4 | if len(word) > len(longest_word): 5 | longest_word = word 6 | return longest_word 7 | 8 | 9 | if __name__ == '__main__': 10 | n = int(input()) 11 | sentence = input() 12 | word = find_longest_word(sentence) 13 | print(word) 14 | print(len(word)) 15 | -------------------------------------------------------------------------------- /sprint3/a_bracket_gen.py: -------------------------------------------------------------------------------- 1 | def bracket_gen(n: int, left: int = 0, right: int = 0, s: str = ''): 2 | if left == right == n: 3 | yield s 4 | if left < n: 5 | yield from bracket_gen(n, left + 1, right, f'{s}(') 6 | if right < left: 7 | yield from bracket_gen(n, left, right + 1, f'{s})') 8 | 9 | 10 | if __name__ == '__main__': 11 | n = int(input()) 12 | for s in bracket_gen(n): 13 | print(s) 14 | -------------------------------------------------------------------------------- /bonus2/i_different_search_trees.py: -------------------------------------------------------------------------------- 1 | from functools import cache 2 | 3 | 4 | @cache 5 | def count_distinct(n: int) -> int: 6 | """Count distinct search trees with n unique numbers.""" 7 | if n == 0: 8 | return 1 9 | return sum( 10 | count_distinct(i - 1) * count_distinct(n - i) for i in range(1, n + 1) 11 | ) 12 | 13 | 14 | if __name__ == '__main__': 15 | n = int(input()) 16 | print(count_distinct(n)) 17 | -------------------------------------------------------------------------------- /bonus2/l_sieve_down.py: -------------------------------------------------------------------------------- 1 | def sift_down(heap: list[int], index: int) -> int: 2 | left = 2 * index 3 | right = left + 1 4 | if left >= len(heap): 5 | return index 6 | 7 | largest = right if right < len(heap) and heap[right] > heap[left] else left 8 | if heap[index] >= heap[largest]: 9 | return index 10 | 11 | heap[index], heap[largest] = heap[largest], heap[index] 12 | return sift_down(heap, largest) 13 | -------------------------------------------------------------------------------- /sprint1/h_binary.py: -------------------------------------------------------------------------------- 1 | from itertools import zip_longest 2 | 3 | 4 | def binary_sum(a: str, b: str) -> str: 5 | result, carry_over = '', 0 6 | for m, n in zip_longest(a[::-1], b[::-1], fillvalue='0'): 7 | carry_over, digit = divmod(int(m) + int(n) + carry_over, 2) 8 | result += str(digit) 9 | return '1' * carry_over + result[::-1] 10 | 11 | 12 | if __name__ == '__main__': 13 | print(binary_sum(input(), input())) 14 | -------------------------------------------------------------------------------- /sprint3/h_large_number.py: -------------------------------------------------------------------------------- 1 | from functools import cmp_to_key 2 | 3 | 4 | def compare(a: str, b: str) -> int: 5 | return -1 if a + b < b + a else 1 if a + b > b + a else 0 6 | 7 | 8 | def largest_number(numbers: list[str]) -> str: 9 | return ''.join(sorted(numbers, key=cmp_to_key(compare), reverse=True)) 10 | 11 | 12 | if __name__ == '__main__': 13 | _ = int(input()) 14 | numbers = input().split() 15 | print(largest_number(numbers)) 16 | -------------------------------------------------------------------------------- /sprint1/j_factorization.py: -------------------------------------------------------------------------------- 1 | def factorization(n: int): 2 | if n <= 1: 3 | yield 1 4 | return 5 | factor = 2 6 | while factor**2 <= n: 7 | if n % factor == 0: 8 | yield factor 9 | n //= factor 10 | else: 11 | factor += 1 12 | if n > 1: 13 | yield n 14 | 15 | 16 | if __name__ == '__main__': 17 | n = int(input()) 18 | print(' '.join(map(str, factorization(n)))) 19 | -------------------------------------------------------------------------------- /sprint1/lesson_average.py: -------------------------------------------------------------------------------- 1 | def moving_average(a, k): 2 | current_sum = sum(a[0:k]) 3 | result = [current_sum / k] 4 | for i in range(len(a) - k): 5 | current_sum += a[i + k] - a[i] 6 | result.append(current_sum / k) 7 | return result 8 | 9 | 10 | if __name__ == '__main__': 11 | n = int(input()) 12 | a = list(map(int, input().split())) 13 | k = int(input()) 14 | print(' '.join(map(str, moving_average(a, k)))) 15 | -------------------------------------------------------------------------------- /yaintern/b_even_fence.py: -------------------------------------------------------------------------------- 1 | def min_minmax_diff(lengths: list[int], extra: int) -> int: 2 | lengths.sort() 3 | count = len(lengths) - extra 4 | diffs = sorted( 5 | lengths[i + count - 1] - lengths[i] for i in range(extra + 1) 6 | ) 7 | return diffs[0] 8 | 9 | 10 | if __name__ == '__main__': 11 | count, extra = (int(s) for s in input().split()) 12 | lengths = [int(s) for s in input().split()] 13 | print(min_minmax_diff(lengths, extra)) 14 | -------------------------------------------------------------------------------- /sprint1/final_sleight_of_hand.py: -------------------------------------------------------------------------------- 1 | """ID посылки: 87592136.""" 2 | 3 | from collections import Counter 4 | from itertools import chain 5 | 6 | 7 | def max_score(field: list[str], k: int) -> int: 8 | counter = Counter(chain(*field)) 9 | counter.pop('.', None) 10 | return sum(k * 2 >= value for value in counter.values()) 11 | 12 | 13 | if __name__ == '__main__': 14 | k = int(input()) 15 | field = [input() for _ in range(4)] 16 | print(max_score(field, k)) 17 | -------------------------------------------------------------------------------- /sprint1/lesson_zipper.py: -------------------------------------------------------------------------------- 1 | from itertools import chain 2 | 3 | 4 | def zipper(a: list[int], b: list[int]) -> list[int]: 5 | return list(chain(*zip(a, b))) 6 | 7 | 8 | def read_input() -> tuple[list[int], list[int]]: 9 | _ = int(input()) 10 | a = list(map(int, input().strip().split())) 11 | b = list(map(int, input().strip().split())) 12 | return a, b 13 | 14 | 15 | if __name__ == '__main__': 16 | a, b = read_input() 17 | print(' '.join(map(str, zipper(a, b)))) 18 | -------------------------------------------------------------------------------- /sprint2/a_monitoring.py: -------------------------------------------------------------------------------- 1 | Matrix = list[list[int]] 2 | 3 | 4 | def transpose(matrix: Matrix, rows: int, cols: int) -> Matrix: 5 | return [[matrix[i][j] for i in range(rows)] for j in range(cols)] 6 | 7 | 8 | if __name__ == '__main__': 9 | rows = int(input()) 10 | cols = int(input()) 11 | matrix = [[int(s) for s in input().split()] for _ in range(rows)] 12 | transposed = transpose(matrix, rows, cols) 13 | for row in transposed: 14 | print(' '.join(map(str, row))) 15 | -------------------------------------------------------------------------------- /sprint2/b_todo.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, value, next_item: 'Node | None' = None): 3 | self.value = value 4 | self.next_item = next_item 5 | 6 | 7 | def print_linked_list(node: Node | None): 8 | while node: 9 | print(node.value) 10 | node = node.next_item 11 | 12 | 13 | if __name__ == '__main__': 14 | n = int(input()) 15 | node = None 16 | for _ in range(n): 17 | node = Node(value=input(), next_item=node) 18 | print_linked_list(node) 19 | -------------------------------------------------------------------------------- /sprint3/i_conference_fans.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | def top_k_schools(schools: list[int], k: int) -> list[int]: 5 | counter = Counter(schools) 6 | by_freq_value = lambda t: (-t[1], t[0]) 7 | top_schools = sorted(counter.items(), key=by_freq_value) 8 | return [t[0] for t in top_schools][:k] 9 | 10 | 11 | if __name__ == '__main__': 12 | n = int(input()) 13 | schools = list(map(int, input().split())) 14 | k = int(input()) 15 | print(*top_k_schools(schools, k)) 16 | -------------------------------------------------------------------------------- /shbr2/a_repeating_number.py: -------------------------------------------------------------------------------- 1 | def has_repeats_at_dist(a: list[int], max_dist: int) -> bool: 2 | last_seen = {} 3 | for index, item in enumerate(a): 4 | if item in last_seen and index - last_seen[item] <= max_dist: 5 | return True 6 | last_seen[item] = index 7 | return False 8 | 9 | 10 | if __name__ == '__main__': 11 | n, max_dist = (int(s) for s in input().split()) 12 | a = [int(s) for s in input().split()] 13 | print('YES' if has_repeats_at_dist(a, max_dist) else 'NO') 14 | -------------------------------------------------------------------------------- /bonus2/f_max_depth.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | value, 8 | left: Optional['Node'] = None, 9 | right: Optional['Node'] = None, 10 | ): 11 | self.value = value 12 | self.left = left 13 | self.right = right 14 | 15 | 16 | def solution(root: Node | None) -> int: 17 | """Find max depth of a tree.""" 18 | if not root: 19 | return 0 20 | return 1 + max(solution(root.left), solution(root.right)) 21 | -------------------------------------------------------------------------------- /shbr5/d_delete_string.py: -------------------------------------------------------------------------------- 1 | def min_length_after_ops(s: str) -> int: 2 | left, right = 0, len(s) - 1 3 | while left < right: 4 | if s[left] != s[right]: 5 | return right - left + 1 6 | char = s[left] 7 | while left < len(s) and s[left] == char: 8 | left += 1 9 | while right >= 0 and s[right] == char: 10 | right -= 1 11 | return 0 if left > right else 1 12 | 13 | 14 | if __name__ == '__main__': 15 | s = input() 16 | print(min_length_after_ops(s)) 17 | -------------------------------------------------------------------------------- /shbr1/b_canonical_path.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | 4 | def get_canonical_path_cheat(s: str) -> str: 5 | return str(Path(s).resolve()) 6 | 7 | 8 | def get_canonical_path(s: str) -> str: 9 | parts = [] 10 | for part in s.split('/'): 11 | if part == '..' and parts: 12 | parts.pop() 13 | elif part not in {'', '.', '..'}: 14 | parts.append(part) 15 | return '/' + '/'.join(parts) 16 | 17 | 18 | if __name__ == '__main__': 19 | path = input() 20 | print(get_canonical_path(path)) 21 | -------------------------------------------------------------------------------- /sprint3/d_cookies.py: -------------------------------------------------------------------------------- 1 | def count_happy(greed: list[int], cookies: list[int]): 2 | greed.sort() 3 | cookies.sort() 4 | i = 0 5 | for cookie in cookies: 6 | if cookie < greed[i]: 7 | continue 8 | i += 1 9 | if i == len(greed): 10 | break 11 | return i 12 | 13 | 14 | if __name__ == '__main__': 15 | n = int(input()) 16 | greed = list(map(int, input().split())) 17 | m = int(input()) 18 | cookies = list(map(int, input().split())) 19 | print(count_happy(greed, cookies)) 20 | -------------------------------------------------------------------------------- /bonus1/f_anagram_grouping.py: -------------------------------------------------------------------------------- 1 | from collections import Counter, defaultdict 2 | 3 | 4 | def anagram_groups(words: list[str]) -> list[list[int]]: 5 | groups = defaultdict(list) 6 | for index, word in enumerate(words): 7 | key = frozenset(Counter(word).items()) 8 | groups[key].append(index) 9 | return list(groups.values()) 10 | 11 | 12 | if __name__ == '__main__': 13 | n = int(input()) 14 | words = input().split() 15 | groups = anagram_groups(words) 16 | print(*(' '.join(map(str, group)) for group in groups), sep='\n') 17 | -------------------------------------------------------------------------------- /sprint2/h_bracket_sequence.py: -------------------------------------------------------------------------------- 1 | def is_correct_bracket_seq(s: str) -> bool: 2 | unclosed_brackets = [] 3 | for ch in s: 4 | if ch in '[({': 5 | unclosed_brackets.append(ch) 6 | continue 7 | if not unclosed_brackets: 8 | return False 9 | last_open = unclosed_brackets.pop() 10 | if (last_open, ch) not in {('[', ']'), ('(', ')'), ('{', '}')}: 11 | return False 12 | return not unclosed_brackets 13 | 14 | 15 | if __name__ == '__main__': 16 | s = input() 17 | print(is_correct_bracket_seq(s)) 18 | -------------------------------------------------------------------------------- /shbr1/d_time_difference.py: -------------------------------------------------------------------------------- 1 | from itertools import pairwise, starmap 2 | from operator import sub 3 | 4 | DAY = 24 * 60 5 | 6 | 7 | def to_minutes(time: str) -> int: 8 | hours, minutes = map(int, time.split(':')) 9 | return hours * 60 + minutes 10 | 11 | 12 | def min_gap(arrivals: list[str]) -> int: 13 | minutes = sorted(map(to_minutes, arrivals)) 14 | minutes.append(DAY + minutes[0]) 15 | return min(starmap(sub, pairwise(reversed(minutes)))) 16 | 17 | 18 | if __name__ == '__main__': 19 | n = int(input()) 20 | arrivals = input().split() 21 | print(min_gap(arrivals)) 22 | -------------------------------------------------------------------------------- /sprint1/k_list_form.py: -------------------------------------------------------------------------------- 1 | ListForm = list[int] 2 | 3 | 4 | def parse(s: str) -> ListForm: 5 | return [int(ch) for ch in s.split()] 6 | 7 | 8 | def to_digits(n: int) -> ListForm: 9 | return [int(ch) for ch in str(n)] 10 | 11 | 12 | def from_digits(a: ListForm) -> int: 13 | return int(''.join(map(str, a))) 14 | 15 | 16 | def special_sum(x: ListForm, k: int) -> ListForm: 17 | return to_digits(from_digits(x) + k) 18 | 19 | 20 | if __name__ == '__main__': 21 | n = int(input()) 22 | x = parse(input()) 23 | k = int(input()) 24 | print(' '.join(map(str, special_sum(x, k)))) 25 | -------------------------------------------------------------------------------- /sprint3/c_subsequence.py: -------------------------------------------------------------------------------- 1 | def is_subsequence(a: str, b: str): 2 | i = 0 3 | for ch in b: 4 | if a[i] == ch: 5 | i += 1 6 | if i == len(a): 7 | return True 8 | return False 9 | 10 | 11 | def is_subsequence_rec(a: str, b: str): 12 | if not a: 13 | return True 14 | if not b: 15 | return False 16 | if a[0] == b[0]: 17 | return is_subsequence_rec(a[1:], b[1:]) 18 | return is_subsequence_rec(a, b[1:]) 19 | 20 | 21 | if __name__ == '__main__': 22 | a, b = input(), input() 23 | print(is_subsequence(a, b)) 24 | -------------------------------------------------------------------------------- /shbr5/a_bulls_and_cows.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | def count_bulls_and_cows(a: str, b: str) -> tuple[int, int]: 5 | bulls, cows = 0, 0 6 | counter = Counter(a) 7 | for i in range(len(a)): 8 | if a[i] == b[i]: 9 | bulls += 1 10 | counter[b[i]] -= 1 11 | for i in range(len(a)): 12 | if a[i] != b[i] and counter[b[i]]: 13 | cows += 1 14 | counter[b[i]] -= 1 15 | return bulls, cows 16 | 17 | 18 | if __name__ == '__main__': 19 | a, b = input(), input() 20 | print(*count_bulls_and_cows(a, b), sep='\n') 21 | -------------------------------------------------------------------------------- /bonus1/e_substrings.py: -------------------------------------------------------------------------------- 1 | def max_substring_with_no_repeatitions(s: str) -> int: 2 | max_len, chars = 0, {} 3 | for i, char in enumerate(s): 4 | if char not in chars: 5 | chars[char] = i 6 | continue 7 | max_len = max(len(chars), max_len) 8 | for seen_char in list(chars.keys()): 9 | del chars[seen_char] 10 | if char == seen_char: 11 | break 12 | chars[char] = i 13 | return max(len(chars), max_len) 14 | 15 | 16 | if __name__ == '__main__': 17 | s = input() 18 | print(max_substring_with_no_repeatitions(s)) 19 | -------------------------------------------------------------------------------- /shbr2/e_deleting_numbers.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from itertools import pairwise 3 | 4 | 5 | def min_to_delete(a: list[int]) -> int: 6 | counter = Counter(a) 7 | unique_nums = sorted(counter.keys()) 8 | if len(unique_nums) < 2: 9 | return 0 10 | max_len = 1 11 | for x, y in pairwise(unique_nums): 12 | if abs(x - y) <= 1: 13 | max_len = max(counter[x] + counter[y], max_len) 14 | return len(a) - max_len 15 | 16 | 17 | if __name__ == '__main__': 18 | n = int(input()) 19 | a = [int(s) for s in input().split()] 20 | print(min_to_delete(a)) 21 | -------------------------------------------------------------------------------- /sprint3/j_bubble.py: -------------------------------------------------------------------------------- 1 | def run_bubble_sort(a: list[int]) -> None: 2 | is_sorted_at_start = True 3 | is_sorted = False 4 | while not is_sorted: 5 | is_sorted = True 6 | for i in range(len(a) - 1): 7 | if a[i] > a[i + 1]: 8 | a[i], a[i + 1] = a[i + 1], a[i] 9 | is_sorted = is_sorted_at_start = False 10 | if not is_sorted: 11 | print(*a) 12 | if is_sorted_at_start: 13 | print(*a) 14 | 15 | 16 | if __name__ == '__main__': 17 | n = int(input()) 18 | a = list(map(int, input().split())) 19 | run_bubble_sort(a) 20 | -------------------------------------------------------------------------------- /yaintern/a_usb.py: -------------------------------------------------------------------------------- 1 | def min_price( 2 | port_count: int, gadget_count: int, price_c2: int, price_c5: int 3 | ) -> int: 4 | req_ports = gadget_count - port_count 5 | if req_ports <= 0: 6 | return 0 7 | req_sets_of_four, req_rest = divmod(req_ports, 4) 8 | return req_sets_of_four * min(4 * price_c2, price_c5) + min( 9 | req_rest * price_c2, price_c5 10 | ) 11 | 12 | 13 | if __name__ == '__main__': 14 | port_count, gadget_count, price_c2, price_c5 = ( 15 | int(input()) for _ in range(4) 16 | ) 17 | print(min_price(port_count, gadget_count, price_c2, price_c5)) 18 | -------------------------------------------------------------------------------- /sprint3/p_partial_sort.py: -------------------------------------------------------------------------------- 1 | def count_max_blocks(a: list[int]) -> int: 2 | blocks = [] 3 | for n in a: 4 | cur_min, cur_max = n, n 5 | while blocks: 6 | prev_min, prev_max = blocks[-1] 7 | if prev_max <= cur_min: 8 | break 9 | blocks.pop() 10 | cur_min = min(prev_min, cur_min) 11 | cur_max = max(prev_max, cur_max) 12 | blocks.append((cur_min, cur_max)) 13 | return len(blocks) 14 | 15 | 16 | if __name__ == '__main__': 17 | _ = int(input()) 18 | nums = list(map(int, input().split())) 19 | print(count_max_blocks(nums)) 20 | -------------------------------------------------------------------------------- /shbr4/b_guests.py: -------------------------------------------------------------------------------- 1 | def find_dates(friends: list[list[int]]) -> list[list[int]]: 2 | dates = sorted((date, index) for index, date in enumerate(friends)) 3 | next_start = 0 4 | result = [[-1, -1] for _ in range(len(friends))] 5 | for (start, end), index in dates: 6 | if next_start > end: 7 | continue 8 | result[index] = [max(next_start, start), end] 9 | next_start = end + 1 10 | return result 11 | 12 | 13 | if __name__ == '__main__': 14 | n = int(input()) 15 | friends = [[int(s) for s in input().split()] for _ in range(n)] 16 | for date in find_dates(friends): 17 | print(*date) 18 | -------------------------------------------------------------------------------- /bonus2/a_lamps.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | value, 8 | left: Optional['Node'] = None, 9 | right: Optional['Node'] = None, 10 | ): 11 | self.value = value 12 | self.left = left 13 | self.right = right 14 | 15 | 16 | def solution(root: Node) -> int: 17 | """DFS for max value in a tree.""" 18 | stack, max_value = [root], root.value 19 | while stack: 20 | node = stack.pop() 21 | max_value = max(node.value, max_value) 22 | stack.extend(filter(None, (node.left, node.right))) 23 | return max_value 24 | -------------------------------------------------------------------------------- /shbr4/a_groups_and_rooms.py: -------------------------------------------------------------------------------- 1 | def max_groups(groups: list[int], rooms: list[int]) -> int: 2 | groups.sort() 3 | rooms.sort() 4 | group_i, room_i = 0, 0 5 | while group_i < len(groups): 6 | while room_i < len(rooms) and groups[group_i] > rooms[room_i]: 7 | room_i += 1 8 | if room_i >= len(rooms): 9 | break 10 | room_i += 1 11 | group_i += 1 12 | return group_i 13 | 14 | 15 | if __name__ == '__main__': 16 | _ = input() 17 | groups = [int(s) for s in input().split()] 18 | _ = input() 19 | rooms = [int(s) for s in input().split()] 20 | print(max_groups(groups, rooms)) 21 | -------------------------------------------------------------------------------- /sprint3/k_merge_sort.py: -------------------------------------------------------------------------------- 1 | def merge_sort(a: list[int], left: int, right: int) -> None: 2 | if right - left <= 1: 3 | return 4 | 5 | mid = (left + right) // 2 6 | merge_sort(a, left, mid) 7 | merge_sort(a, mid, right) 8 | a[left:right] = merge(a, left, mid, right) 9 | 10 | 11 | def merge(a: list[int], left: int, mid: int, right: int) -> list[int]: 12 | result = [] 13 | i, j = left, mid 14 | while i < mid or j < right: 15 | if i < mid and (j == right or a[i] <= a[j]): 16 | result.append(a[i]) 17 | i += 1 18 | else: 19 | result.append(a[j]) 20 | j += 1 21 | return result 22 | -------------------------------------------------------------------------------- /shbr3/a_count_positive.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Iterable 2 | from itertools import accumulate 3 | 4 | 5 | def count_positive(a: list[int], queries: list[list[int]]) -> Iterable[int]: 6 | start_counts = list(accumulate(a, lambda acc, x: acc + (x > 0), initial=0)) 7 | for left, right in queries: 8 | yield start_counts[right] - start_counts[left - 1] 9 | 10 | 11 | def main(): 12 | _ = int(input()) 13 | a = [int(s) for s in input().split()] 14 | query_count = int(input()) 15 | queries = [[int(s) for s in input().split()] for _ in range(query_count)] 16 | print(*count_positive(a, queries), sep='\n') 17 | 18 | 19 | if __name__ == '__main__': 20 | main() 21 | -------------------------------------------------------------------------------- /bonus2/d_tree_twins.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | value, 8 | left: Optional['Node'] = None, 9 | right: Optional['Node'] = None, 10 | ): 11 | self.value = value 12 | self.left = left 13 | self.right = right 14 | 15 | 16 | def solution(root1: Node | None, root2: Node | None) -> bool: 17 | """Check if two trees are identical.""" 18 | return not any((root1, root2)) or ( 19 | (root1 is not None and root2 is not None) 20 | and (root1.value == root2.value) 21 | and solution(root1.left, root2.left) 22 | and solution(root1.right, root2.right) 23 | ) 24 | -------------------------------------------------------------------------------- /sprint3/e_houses.py: -------------------------------------------------------------------------------- 1 | from itertools import accumulate 2 | 3 | 4 | def count_houses(houses: list[int], budget: int) -> int: 5 | houses.sort() 6 | total = count = 0 7 | for house in houses: 8 | total += house 9 | if total > budget: 10 | break 11 | count += 1 12 | return count 13 | 14 | 15 | def count_houses_acc(houses: list[int], budget: int) -> int: 16 | sums = accumulate(sorted(houses)) 17 | return next((i for i, acc in enumerate(sums) if acc > budget), len(houses)) 18 | 19 | 20 | if __name__ == '__main__': 21 | _, budget = map(int, input().split()) 22 | houses = list(map(int, input().split())) 23 | print(count_houses(houses, budget)) 24 | -------------------------------------------------------------------------------- /sprint1/final_nearest_zero.py: -------------------------------------------------------------------------------- 1 | """ID посылки: 87592079.""" 2 | 3 | from math import inf 4 | 5 | 6 | def nearest_zeroes(numbers: list[int]) -> list[int]: 7 | distances = [] 8 | last_zero = -1 9 | for i, value in enumerate(numbers): 10 | if value: 11 | distances.append(i - last_zero if last_zero != -1 else inf) 12 | continue 13 | for j in range(last_zero + 1, i): 14 | distances[j] = min(distances[j], i - j) 15 | distances.append(0) 16 | last_zero = i 17 | return distances 18 | 19 | 20 | if __name__ == '__main__': 21 | n = int(input()) 22 | houses = [int(x) for x in input().split()] 23 | print(*nearest_zeroes(houses)) 24 | -------------------------------------------------------------------------------- /shbr1/e_break_palindrome.py: -------------------------------------------------------------------------------- 1 | def break_palindrome(s: str) -> str: 2 | if len(s) <= 1: 3 | return '' 4 | chars = list(s) 5 | for i, char in enumerate(s): 6 | if char != 'a' and (len(s) % 2 == 0 or i != len(s) // 2): 7 | chars[i] = 'a' 8 | break 9 | else: # no break 10 | chars[-1] = 'b' 11 | return ''.join(chars) 12 | 13 | 14 | def break_palindrome_func(s: str) -> str: 15 | n = len(s) 16 | if n <= 1: 17 | return '' 18 | i = next(filter(lambda i: s[i] != 'a', range(n // 2)), n - 1) 19 | return s[:i] + ('a' if i != n - 1 else 'b') + s[i + 1 :] 20 | 21 | 22 | if __name__ == '__main__': 23 | s = input() 24 | print(break_palindrome(s)) 25 | -------------------------------------------------------------------------------- /bonus2/k_show_range.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | left: Optional['Node'] = None, 8 | right: Optional['Node'] = None, 9 | value=0, 10 | ): 11 | self.left = left 12 | self.right = right 13 | self.value = value 14 | 15 | 16 | def print_range(node: Node | None, left, right): 17 | """Print all nodes in a search tree that are in a given range.""" 18 | if not node: 19 | return 20 | if node.value >= left: 21 | print_range(node.left, left, right) 22 | if left <= node.value <= right: 23 | print(node.value) 24 | if node.value <= right: 25 | print_range(node.right, left, right) 26 | -------------------------------------------------------------------------------- /shbr4/c_socks.py: -------------------------------------------------------------------------------- 1 | from itertools import accumulate 2 | from operator import add 3 | 4 | MAX_VALUE = 10_000 5 | 6 | 7 | def thickness(socks: list[list[int]], points: list[int]) -> list[int]: 8 | counter = [0] * (MAX_VALUE + 1) 9 | for start, end in socks: 10 | counter[start - 1] += 1 11 | counter[end] -= 1 12 | counter = list(accumulate(counter, add)) 13 | return [counter[p - 1] for p in points] 14 | 15 | 16 | if __name__ == '__main__': 17 | max_value, sock_count, point_count = (int(s) for s in input().split()) 18 | socks = [[int(s) for s in input().split()] for _ in range(sock_count)] 19 | points = [int(input()) for _ in range(point_count)] 20 | print(*thickness(socks, points), sep='\n') 21 | -------------------------------------------------------------------------------- /sprint1/lesson_twosum.py: -------------------------------------------------------------------------------- 1 | def twosum(a, k): 2 | for i in range(len(a)): 3 | for j in range(i + 1, len(a)): 4 | if a[i] + a[j] == k: 5 | return a[i], a[j] 6 | return [None] 7 | 8 | 9 | def twosum_with_sort(a, k): 10 | a.sort() 11 | 12 | left = 0 13 | right = len(a) - 1 14 | while left < right: 15 | current_sum = a[left] + a[right] 16 | if current_sum == k: 17 | return a[left], a[right] 18 | if current_sum < k: 19 | left += 1 20 | else: 21 | right -= 1 22 | return [None] 23 | 24 | 25 | n = int(input()) 26 | a = list(map(int, input().split())) 27 | k = int(input()) 28 | print(' '.join(map(str, twosum(a, k)))) 29 | -------------------------------------------------------------------------------- /sprint3/b_combinations.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | 3 | LETTERS = { 4 | '2': 'abc', 5 | '3': 'def', 6 | '4': 'ghi', 7 | '5': 'jkl', 8 | '6': 'mno', 9 | '7': 'pqrs', 10 | '8': 'tuv', 11 | '9': 'wxyz', 12 | } 13 | 14 | 15 | def phone_combinations(phone: str): 16 | yield from map(''.join, product(*map(LETTERS.get, phone))) # type: ignore 17 | 18 | 19 | def phone_combinations_rec(phone: str, prefix: str = ''): 20 | if len(prefix) == len(phone): 21 | yield prefix 22 | return 23 | for letter in LETTERS[phone[len(prefix)]]: 24 | yield from phone_combinations_rec(phone, prefix + letter) 25 | 26 | 27 | if __name__ == '__main__': 28 | print(*phone_combinations(input())) 29 | -------------------------------------------------------------------------------- /sprint3/n_flowerbeds.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Iterable 2 | 3 | 4 | def union(segments: list[list[int]]) -> Iterable[list[int]]: 5 | segments.sort() 6 | left = 0 7 | while left < len(segments): 8 | segment_start, segment_end = segments[left] 9 | right = left + 1 10 | while right < len(segments) and segments[right][0] <= segment_end: 11 | segment_end = max(segment_end, segments[right][1]) 12 | right += 1 13 | yield [segment_start, segment_end] 14 | left = right 15 | 16 | 17 | if __name__ == '__main__': 18 | n = int(input()) 19 | segments = [list(map(int, input().split())) for _ in range(n)] 20 | for segment in union(segments): 21 | print(*segment) 22 | -------------------------------------------------------------------------------- /shbr1/c_buy_sell.py: -------------------------------------------------------------------------------- 1 | def find_max_difference(a: list[int]) -> tuple[int, int]: 2 | max_left, max_right, max_profit_coef = 0, 0, 1 3 | left = right = 0 4 | for i in range(1, len(a)): 5 | if a[i] < a[left]: 6 | left = i 7 | elif a[i] > a[left] and (right <= left or a[i] > a[right]): 8 | right = i 9 | profit_coef = a[right] / a[left] 10 | if profit_coef > max_profit_coef: 11 | max_left, max_right = left + 1, right + 1 12 | max_profit_coef = profit_coef 13 | return max_left, max_right 14 | 15 | 16 | if __name__ == '__main__': 17 | n = int(input()) 18 | numbers = list(map(int, input().split())) 19 | print(*find_max_difference(numbers), sep=' ') 20 | -------------------------------------------------------------------------------- /shbr4/e_filepath.py: -------------------------------------------------------------------------------- 1 | def find_path(filetree: list[str], filename: str) -> str: 2 | stack, prev_depth, prev_name = [], 0, None 3 | for s in filetree: 4 | name = s.strip() 5 | depth = len(s) - len(name) 6 | if depth > prev_depth: 7 | stack.append(prev_name) 8 | elif depth < prev_depth: 9 | for _ in range(prev_depth - depth): 10 | stack.pop() 11 | if name == filename: 12 | return (f'/{"/".join(stack)}/' if stack else '/') + filename 13 | prev_depth = depth 14 | prev_name = name 15 | return '' 16 | 17 | 18 | if __name__ == '__main__': 19 | filename = input() 20 | filetree = [input() for _ in range(int(input()))] 21 | print(find_path(filetree, filename)) 22 | -------------------------------------------------------------------------------- /sprint1/c_neighbors.py: -------------------------------------------------------------------------------- 1 | Matrix = list[list[int]] 2 | 3 | 4 | def neighbors(matrix: Matrix, row: int, col: int) -> list[int]: 5 | rows = len(matrix) 6 | cols = len(matrix[0]) 7 | deltas = ((-1, 0), (1, 0), (0, -1), (0, 1)) 8 | coords = (tuple(map(sum, zip((row, col), d))) for d in deltas) 9 | in_range = lambda t: 0 <= t[0] < rows and 0 <= t[1] < cols 10 | get = lambda t: matrix[t[0]][t[1]] 11 | return sorted(map(get, filter(in_range, coords))) 12 | 13 | 14 | if __name__ == '__main__': 15 | rows = int(input()) 16 | cols = int(input()) 17 | matrix = [list(map(int, input().split())) for _ in range(rows)] 18 | row = int(input()) 19 | col = int(input()) 20 | result = neighbors(matrix, row, col) 21 | print(' '.join(map(str, result))) 22 | -------------------------------------------------------------------------------- /bonus1/b_crash_me.py: -------------------------------------------------------------------------------- 1 | from functools import reduce 2 | from itertools import combinations_with_replacement 3 | from string import ascii_lowercase 4 | 5 | a = 1_000 6 | m = 123_987_123 7 | 8 | 9 | def polynomial_hash(s: str) -> int: 10 | return reduce(lambda acc, ch: (acc * a + ord(ch)) % m, s, 0) 11 | 12 | 13 | def find_collision() -> tuple[str, str]: 14 | strings, length = {}, 1 15 | while True: 16 | combs = combinations_with_replacement(ascii_lowercase, length) 17 | for s in map(''.join, combs): 18 | hash_val = polynomial_hash(s) 19 | if hash_val in strings: 20 | return s, strings[hash_val] 21 | strings[hash_val] = s 22 | length += 1 23 | 24 | 25 | if __name__ == '__main__': 26 | print(*find_collision(), sep='\n') 27 | -------------------------------------------------------------------------------- /bonus1/g_competition.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Sequence 2 | 3 | 4 | def longest_balanced(sequence: Sequence[str]) -> int: 5 | balance = 0 6 | max_index = -1 7 | index_by_balance = {balance: max_index} 8 | max_length = 0 9 | 10 | for i, item in enumerate(sequence): 11 | balance += 1 if item == '1' else -1 12 | if balance not in index_by_balance: 13 | index_by_balance[balance] = i 14 | continue 15 | length = i - index_by_balance[balance] 16 | if length > max_length: 17 | max_length = length 18 | max_index = index_by_balance[balance] + 1 19 | 20 | return max_length 21 | 22 | 23 | if __name__ == '__main__': 24 | n = int(input()) 25 | strings = input().split() 26 | print(longest_balanced(strings)) 27 | -------------------------------------------------------------------------------- /bonus2/h_num_paths.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | value, 8 | left: Optional['Node'] = None, 9 | right: Optional['Node'] = None, 10 | ): 11 | self.value = value 12 | self.left = left 13 | self.right = right 14 | 15 | 16 | def solution(root: Node) -> int: 17 | """Find sum of all path "numbers" in a tree.""" 18 | 19 | def traverse(node: Node | None, prefix: str = ''): 20 | if not node: 21 | return 22 | new_prefix = prefix + str(node.value) 23 | if not node.left and not node.right: 24 | yield new_prefix 25 | yield from traverse(node.left, new_prefix) 26 | yield from traverse(node.right, new_prefix) 27 | 28 | return sum(map(int, traverse(root))) 29 | -------------------------------------------------------------------------------- /bonus2/j_add_node.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | left: Optional['Node'] = None, 8 | right: Optional['Node'] = None, 9 | value=0, 10 | ): 11 | self.left = left 12 | self.right = right 13 | self.value = value 14 | 15 | 16 | def insert(root: Node, key) -> Node: 17 | """Insert a node into a search tree.""" 18 | node = root 19 | while node: 20 | if key < node.value: 21 | if not node.left: 22 | node.left = Node(None, None, key) 23 | break 24 | node = node.left 25 | else: 26 | if not node.right: 27 | node.right = Node(None, None, key) 28 | break 29 | node = node.right 30 | return root 31 | -------------------------------------------------------------------------------- /bonus1/h_weird_comparison.py: -------------------------------------------------------------------------------- 1 | def weird_compare(s: str, t: str) -> bool: 2 | if len(s) != len(t): 3 | return False 4 | # Store indices of first occurence and count 5 | s_counter, t_counter = {}, {} 6 | for i in range(len(s)): 7 | s_char, t_char = s[i], t[i] 8 | if s_char in s_counter and t_char in t_counter: 9 | if s_counter[s_char] != t_counter[t_char]: 10 | return False 11 | s_counter[s_char][1] += 1 12 | t_counter[t_char][1] += 1 13 | elif s_char not in s_counter and t_char not in t_counter: 14 | s_counter[s_char] = [i, 1] 15 | t_counter[t_char] = [i, 1] 16 | else: 17 | return False 18 | return True 19 | 20 | 21 | if __name__ == '__main__': 22 | s, t = input(), input() 23 | print('YES' if weird_compare(s, t) else 'NO') 24 | -------------------------------------------------------------------------------- /sprint2/final_calc.py: -------------------------------------------------------------------------------- 1 | """ID посылки: 87911546.""" 2 | 3 | import operator 4 | 5 | OPERATIONS = { 6 | '+': operator.add, 7 | '-': operator.sub, 8 | '*': operator.mul, 9 | '/': operator.floordiv, 10 | } 11 | 12 | 13 | class Stack: 14 | def __init__(self): 15 | self._items = [] 16 | 17 | def push(self, item): 18 | self._items.append(item) 19 | 20 | def pop(self): 21 | return self._items.pop() 22 | 23 | 24 | def calc(expression: str) -> int: 25 | stack = Stack() 26 | for token in expression.split(): 27 | if token not in OPERATIONS: 28 | stack.push(int(token)) 29 | continue 30 | right, left = stack.pop(), stack.pop() 31 | stack.push(OPERATIONS[token](left, right)) 32 | return stack.pop() 33 | 34 | 35 | if __name__ == '__main__': 36 | print(calc(input())) 37 | -------------------------------------------------------------------------------- /bonus1/c_prefix_hashes.py: -------------------------------------------------------------------------------- 1 | def prefix_hashes(a: int, m: int, s: str) -> list[int]: 2 | # Can be even shorter with itertools.accumulate 3 | result, hash_ = [], 0 4 | for ch in s: 5 | hash_ = (hash_ * a + ord(ch)) % m 6 | result.append(hash_) 7 | return result 8 | 9 | 10 | def substring_hash(a: int, m: int, prefixes: list[int], i: int, j: int) -> int: 11 | if not i: 12 | return prefixes[j] 13 | return (prefixes[j] - pow(a, j - i + 1, m) * prefixes[i - 1]) % m 14 | 15 | 16 | def main(): 17 | a = int(input()) 18 | m = int(input()) 19 | s = input() 20 | count = int(input()) 21 | prefixes = prefix_hashes(a, m, s) 22 | for _ in range(count): 23 | i, j = (int(s) for s in input().split()) 24 | print(substring_hash(a, m, prefixes, i - 1, j - 1)) 25 | 26 | 27 | if __name__ == '__main__': 28 | main() 29 | -------------------------------------------------------------------------------- /shbr5/b_largest_substring.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from itertools import chain, pairwise 3 | 4 | 5 | def find_max_good_substring_length(s: str, min_count: int) -> int: 6 | def rec(left: int, right: int): 7 | counter = defaultdict(list) 8 | for i in range(left, right + 1): 9 | counter[s[i]].append(i) 10 | bad_chars = (lst for lst in counter.values() if len(lst) < min_count) 11 | split_points = [left - 1, *sorted(chain(*bad_chars)), right + 1] 12 | if len(split_points) == 2: 13 | return right - left + 1 14 | return max(rec(a + 1, b - 1) for a, b in pairwise(split_points)) 15 | 16 | return rec(0, len(s) - 1) 17 | 18 | 19 | if __name__ == '__main__': 20 | n, min_count = (int(s) for s in input().split()) 21 | s = input() 22 | print(find_max_good_substring_length(s, min_count)) 23 | -------------------------------------------------------------------------------- /shbr5/e_pitcraft.py: -------------------------------------------------------------------------------- 1 | def count_filled_blocks(heights: list[int]) -> int: 2 | def fill(left: int, right: int, step: int = 1) -> tuple[int, int]: 3 | total, cur_total, prev_max, prev_max_i = 0, 0, 0, -1 4 | for i in range(left, right + step, step): 5 | if prev_max > heights[i]: 6 | cur_total += prev_max - heights[i] 7 | else: 8 | total += cur_total 9 | cur_total = 0 10 | prev_max, prev_max_i = heights[i], i 11 | return total, prev_max_i 12 | 13 | total_ltr, max_i = fill(0, len(heights) - 1) 14 | if max_i == -1: 15 | return total_ltr 16 | total_rtl, _ = fill(len(heights) - 1, max_i, -1) 17 | return total_ltr + total_rtl 18 | 19 | 20 | if __name__ == '__main__': 21 | input() 22 | heights = [int(s) for s in input().split()] 23 | print(count_filled_blocks(heights)) 24 | -------------------------------------------------------------------------------- /sprint2/c_tedious_work.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, value, next_item: 'Node | None' = None): 3 | self.value = value 4 | self.next_item = next_item 5 | 6 | 7 | def print_linked_list(node: Node | None): 8 | while node: 9 | print(node.value) 10 | node = node.next_item 11 | 12 | 13 | def remove(node: Node, pos_to_remove: int): 14 | if pos_to_remove == 0: 15 | return node.next_item 16 | head = prev_node = node 17 | for _ in range(pos_to_remove): 18 | prev_node = node 19 | node = node.next_item 20 | prev_node.next_item = node.next_item 21 | return head 22 | 23 | 24 | if __name__ == '__main__': 25 | n = int(input()) 26 | node = None 27 | for _ in range(n): 28 | node = Node(value=input(), next_item=node) 29 | pos_to_remove = int(input()) 30 | node = remove(node, pos_to_remove) 31 | print_linked_list(node) 32 | -------------------------------------------------------------------------------- /shbr3/d_cows_in_stalls.py: -------------------------------------------------------------------------------- 1 | from math import ceil 2 | 3 | 4 | def can_fit_at_x_distance(a: list[int], total_count: int, x: int) -> bool: 5 | count, last = 1, a[0] 6 | for value in a[1:]: 7 | if value - last < x: 8 | continue 9 | count += 1 10 | if count >= total_count: 11 | return True 12 | last = value 13 | return False 14 | 15 | 16 | def max_min_distance(a: list[int], k: int) -> int: 17 | left, right = 1, a[-1] - a[0] 18 | while left <= right: 19 | if left == right: 20 | break 21 | mid = ceil((left + right) / 2) 22 | if can_fit_at_x_distance(a, k, mid): 23 | left = mid 24 | else: 25 | right = mid - 1 26 | return left 27 | 28 | 29 | if __name__ == '__main__': 30 | n, k = (int(s) for s in input().split()) 31 | a = [int(s) for s in input().split()] 32 | print(max_min_distance(a, k)) 33 | -------------------------------------------------------------------------------- /bonus2/g_max_path.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | value, 8 | left: Optional['Node'] = None, 9 | right: Optional['Node'] = None, 10 | ): 11 | self.value = value 12 | self.left = left 13 | self.right = right 14 | 15 | 16 | def solution(root: Node) -> int: 17 | """Find a sum of the max path in a tree.""" 18 | max_path = [root.value] 19 | 20 | def get_max_from_node(node: Node | None): 21 | if node is None: 22 | return 0 23 | 24 | left_max = max(get_max_from_node(node.left), 0) 25 | right_max = max(get_max_from_node(node.right), 0) 26 | 27 | current_max_path = left_max + node.value + right_max 28 | max_path[0] = max(max_path[0], current_max_path) 29 | 30 | return node.value + max(left_max, right_max) 31 | 32 | get_max_from_node(root) 33 | return max_path[0] 34 | -------------------------------------------------------------------------------- /bonus2/b_balanced_tree.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | value, 8 | left: Optional['Node'] = None, 9 | right: Optional['Node'] = None, 10 | ): 11 | self.value = value 12 | self.left = left 13 | self.right = right 14 | 15 | 16 | def solution(root: Node) -> bool: 17 | def balanced_depth(root: Node | None): 18 | """Return binary tree depth if it's balanced, -1 otherwise.""" 19 | if not root: 20 | return 0 21 | 22 | left_depth = balanced_depth(root.left) 23 | if left_depth == -1: 24 | return -1 25 | right_depth = balanced_depth(root.right) 26 | if right_depth == -1: 27 | return -1 28 | 29 | if abs(left_depth - right_depth) > 1: 30 | return -1 31 | return max(left_depth, right_depth) + 1 32 | 33 | return balanced_depth(root) != -1 34 | -------------------------------------------------------------------------------- /bonus2/c_anagram_tree.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | value, 8 | left: Optional['Node'] = None, 9 | right: Optional['Node'] = None, 10 | ): 11 | self.value = value 12 | self.left = left 13 | self.right = right 14 | 15 | 16 | def solution(root: Node) -> bool: 17 | """Check if a tree is an anagram tree, i.e. vertically symmetric.""" 18 | layer = [root] 19 | while any(layer): 20 | next_layer, values = [], [] 21 | for node in layer: 22 | next_layer += [node.left, node.right] if node else [None, None] 23 | values += [ 24 | node.left.value if node and node.left else None, 25 | node.right.value if node and node.right else None, 26 | ] 27 | if values != values[::-1]: 28 | return False 29 | layer = next_layer 30 | return True 31 | -------------------------------------------------------------------------------- /sprint2/d_caring_mother.py: -------------------------------------------------------------------------------- 1 | from itertools import count 2 | 3 | 4 | class Node: 5 | def __init__(self, value, next_item: 'Node | None' = None): 6 | self.value = value 7 | self.next_item = next_item 8 | 9 | 10 | def find(node: Node | None, value): 11 | i = 0 12 | while node: 13 | if node.value == value: 14 | return i 15 | node = node.next_item 16 | i += 1 17 | return -1 18 | 19 | 20 | def find_iter(head: Node, value): 21 | def node_iter(node): 22 | while node: 23 | yield node 24 | node = node.next_item 25 | 26 | enumerated = zip(count(), node_iter(head)) 27 | return next((i for i, node in enumerated if node.value == value), -1) 28 | 29 | 30 | if __name__ == '__main__': 31 | n = int(input()) 32 | node = None 33 | for _ in range(n): 34 | node = Node(value=input(), next_item=node) 35 | value = input() 36 | print(find(node, value)) 37 | -------------------------------------------------------------------------------- /yaintern/c_repo_inheritance.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__(self, value: int, parent: Optional['Node'] = None): 6 | self.value = value 7 | self.parent = parent 8 | self.depth = parent.depth + 1 if parent else 1 9 | 10 | 11 | def make_tree(parent_values: list[int]) -> list[Node]: 12 | nodes = [Node(0)] 13 | visited: set[Node] = set() 14 | for i, parent_value in enumerate(parent_values): 15 | parent = nodes[parent_value] 16 | visited.add(parent) 17 | node = Node(i + 1, parent) 18 | nodes.append(node) 19 | return [node for node in nodes if node not in visited] 20 | 21 | 22 | def max_depth(leaves: list[Node]) -> int: 23 | index, _ = max(enumerate(leaves), key=lambda t: t[1].depth) 24 | return leaves[index].value 25 | 26 | 27 | if __name__ == '__main__': 28 | n = int(input()) 29 | parent_values = [int(input()) for _ in range(n)] 30 | leaves = make_tree(parent_values) 31 | print(max_depth(leaves)) 32 | -------------------------------------------------------------------------------- /shbr2/c_replacing_words.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from collections.abc import Iterable 3 | from functools import reduce 4 | 5 | LEAF = object() 6 | 7 | 8 | def replace_words(words: list[str], prefixes: list[str]) -> Iterable[str]: 9 | make_tree = lambda: defaultdict(make_tree) 10 | prefix_tree = make_tree() 11 | for prefix in prefixes: 12 | leaf = reduce(lambda tree, ch: tree[ch], prefix, prefix_tree) 13 | leaf[LEAF] = prefix # type: ignore 14 | for word in words: 15 | subtree = prefix_tree 16 | for char in word: 17 | if char not in subtree: 18 | yield word 19 | break 20 | subtree = subtree[char] 21 | if LEAF in subtree: 22 | yield subtree[LEAF] 23 | break 24 | else: # no break 25 | yield word 26 | 27 | 28 | if __name__ == '__main__': 29 | prefixes = input().split() 30 | words = input().split() 31 | print(*replace_words(words, prefixes), sep=' ') 32 | -------------------------------------------------------------------------------- /bonus2/n_tree_partition.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | left: Optional['Node'] = None, 8 | right: Optional['Node'] = None, 9 | value=0, 10 | size=0, 11 | ): 12 | self.left = left 13 | self.right = right 14 | self.value = value 15 | self.size = size 16 | 17 | 18 | def split(root: Node | None, k: int) -> tuple[Node | None, Node | None]: 19 | """Remove the smallest k nodes into a separate search tree.""" 20 | if not root or k == 0: 21 | return None, root 22 | if k >= root.size: 23 | return root, None 24 | if root.left and root.left.size >= k: 25 | small_root, root.left = split(root.left, k) 26 | root.size -= k 27 | else: 28 | small_root = root 29 | left_size = root.left.size if root.left else 0 30 | small_root.right, root = split(root.right, k - left_size - 1) 31 | small_root.size -= root.size if root else 0 32 | return small_root, root 33 | -------------------------------------------------------------------------------- /sprint2/f_stack_max.py: -------------------------------------------------------------------------------- 1 | class StackMax: 2 | def __init__(self): 3 | self._stack = [] 4 | self._max = [] 5 | 6 | def push(self, value: int): 7 | self._stack.append(value) 8 | if not self._max or value >= self._max[-1]: 9 | self._max.append(value) 10 | 11 | def pop(self): 12 | if not self._stack: 13 | return 'error' 14 | value = self._stack.pop() 15 | if self._max[-1] == value: 16 | self._max.pop() 17 | return None 18 | 19 | def get_max(self): 20 | if not self._max: 21 | return 'None' 22 | return self._max[-1] 23 | 24 | 25 | def execute(command: str, stack: StackMax) -> None: 26 | method_name, *args = command.split() 27 | args = list(map(int, args)) 28 | return_value = getattr(stack, method_name)(*args) 29 | if return_value is not None: 30 | print(return_value) 31 | 32 | 33 | if __name__ == '__main__': 34 | n = int(input()) 35 | stack = StackMax() 36 | for _ in range(n): 37 | command = input() 38 | execute(command, stack) 39 | -------------------------------------------------------------------------------- /sprint3/rec_call_tree.py: -------------------------------------------------------------------------------- 1 | def gen_binary(n, prefix, space=''): 2 | if n == 0: 3 | print(f"{space}└───print('{prefix}')") 4 | else: 5 | print(f"{space}├───gen_binary({n - 1}, '{prefix}0 ')") 6 | gen_binary(n - 1, prefix + '0', space + '│ ') 7 | print(f"{space}└───gen_binary({n - 1}, '{prefix}1')") 8 | gen_binary(n - 1, prefix + '1', space + ' ') 9 | 10 | 11 | def gen_brackets(n: int, left: int = 0, right: int = 0, s: str = '', space=''): 12 | if left == right == n: 13 | print(f'{space}└───print: {s}') 14 | if left < n: 15 | div = '├' if right < left else '└' 16 | print(f"{space}{div}───gen_brackets({n}, {left + 1}, {right}, '{s}(')") 17 | div = '│' if right < left else ' ' 18 | gen_brackets(n, left + 1, right, f'{s}(', space + f'{div} ') 19 | if right < left: 20 | print(f"{space}└───gen_brackets({n}, {left}, {right + 1}, '{s})')") 21 | gen_brackets(n, left, right + 1, f'{s})', space + ' ') 22 | 23 | 24 | if __name__ == '__main__': 25 | print("gen_brackets(3, '')") 26 | gen_brackets(3) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Dmitry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sprint2/e_reversed.py: -------------------------------------------------------------------------------- 1 | class DoubleConnectedNode: 2 | def __init__( 3 | self, 4 | value, 5 | next: 'DoubleConnectedNode | None' = None, # noqa: A002 6 | prev: 'DoubleConnectedNode | None' = None, 7 | ): 8 | self.value = value 9 | self.next = next 10 | self.prev = prev 11 | 12 | 13 | def print_linked_list(node: DoubleConnectedNode | None): 14 | while node: 15 | print(node.value) 16 | node = node.next 17 | 18 | 19 | def reverse(node: DoubleConnectedNode | None): 20 | prev_node = node 21 | while node: 22 | node.prev, node.next = node.next, node.prev 23 | prev_node = node 24 | node = node.prev 25 | return prev_node 26 | 27 | 28 | if __name__ == '__main__': 29 | n = int(input()) 30 | head = node = prev_node = None 31 | for _ in range(n): 32 | node = DoubleConnectedNode(value=input(), prev=prev_node) 33 | if not head: 34 | head = node 35 | if prev_node: 36 | prev_node.next = node 37 | prev_node = node 38 | head = reverse(head) 39 | print_linked_list(head) 40 | -------------------------------------------------------------------------------- /sprint3/m_golden_mean.py: -------------------------------------------------------------------------------- 1 | def median(a): 2 | half, remainder = divmod(len(a), 2) 3 | if remainder != 0 or a[half - 1] == a[half]: 4 | return a[half] 5 | avg, remainder = divmod(a[half - 1] + a[half], 2) 6 | return avg if not remainder else avg + 0.5 7 | 8 | 9 | def median_two(a, b): 10 | a_left, a_right = 0, len(a) - 1 11 | b_left, b_right = 0, len(b) - 1 12 | while a_left <= a_right and b_left <= b_right: 13 | if a_left == a_right and b_left == b_right: 14 | avg, remainder = divmod(a[a_left] + b[b_left], 2) 15 | return avg if not remainder else avg + 0.5 16 | if a[a_left] <= b[b_left]: 17 | a_left += 1 18 | else: 19 | b_left += 1 20 | if a[a_right] >= b[b_right]: 21 | a_right -= 1 22 | else: 23 | b_right -= 1 24 | return median( 25 | a[a_left : a_right + 1] 26 | if a_left <= a_right 27 | else b[b_left : b_right + 1] 28 | ) 29 | 30 | 31 | if __name__ == '__main__': 32 | _, _ = input(), input() 33 | a = list(map(int, input().split())) 34 | b = list(map(int, input().split())) 35 | print(median_two(a, b)) 36 | -------------------------------------------------------------------------------- /yaintern/e_priority_admission.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | 4 | @dataclass 5 | class Person: 6 | pos: int 7 | rating: int 8 | programs: list[int] 9 | 10 | 11 | def enroll(program_spots: list[int], priorities: list[list[int]]) -> list[int]: 12 | persons = [ 13 | Person(pos=index, rating=rating, programs=programs) 14 | for index, (rating, _, *programs) in enumerate(priorities) 15 | ] 16 | persons.sort(key=lambda person: person.rating) 17 | program_spots = [0, *program_spots] # programs are counted from 1 18 | admissions = [-1] * len(persons) 19 | for person in persons: 20 | for program in person.programs: 21 | if program_spots[program]: 22 | program_spots[program] -= 1 23 | admissions[person.pos] = program 24 | break 25 | return admissions 26 | 27 | 28 | if __name__ == '__main__': 29 | person_count, program_count = (int(s) for s in input().split()) 30 | program_spots = [int(s) for s in input().split()] 31 | priorities = [ 32 | [int(s) for s in input().split()] for _ in range(person_count) 33 | ] 34 | print(*enroll(program_spots, priorities)) 35 | -------------------------------------------------------------------------------- /sprint3/o_index_difference.py: -------------------------------------------------------------------------------- 1 | from bisect import bisect 2 | 3 | 4 | def count_pairs(a: list[int], max_diff: int) -> int: 5 | """Count pairs with absolute difference less than or equal to max_diff. 6 | 7 | For every element count the number of such pairs in a[i:]. 8 | Bisect returns position of the first element higher than 9 | a[i] + max_diff, which is also the number of elements 10 | less than or equal to a[i] + max_diff. 11 | (i + 1) is the number of elements from start to i. 12 | """ 13 | return sum(bisect(a, n + max_diff) - (i + 1) for i, n in enumerate(a)) 14 | 15 | 16 | def min_diff_by_index(a: list[int], index: int): 17 | a = sorted(a) 18 | min_diff = min(a[i + 1] - a[i] for i in range(len(a) - 1)) 19 | max_diff = a[-1] - a[0] 20 | 21 | while min_diff < max_diff: 22 | mid = (min_diff + max_diff) // 2 23 | if count_pairs(a, mid) < index: 24 | min_diff = mid + 1 25 | else: 26 | max_diff = mid 27 | 28 | return min_diff 29 | 30 | 31 | if __name__ == '__main__': 32 | _ = int(input()) 33 | nums = list(map(int, input().split())) 34 | index = int(input()) 35 | print(min_diff_by_index(nums, index)) 36 | -------------------------------------------------------------------------------- /yaintern/d_digs_and_tiles.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from itertools import pairwise 3 | 4 | 5 | @dataclass 6 | class Dig: 7 | day: int 8 | sidewalk: int 9 | 10 | 11 | def min_sadness( 12 | digs: list[Dig], sidewalk_count: int, laying_count: int 13 | ) -> int: 14 | dig_days_by_sidewalk = [[] for _ in range(sidewalk_count)] 15 | for dig in digs: 16 | dig_days_by_sidewalk[dig.sidewalk - 1].append(dig.day) 17 | sadness_days = [] 18 | for dig_days in dig_days_by_sidewalk: 19 | if not dig_days: 20 | continue 21 | # The final dig on a sidewalk must be covered, 22 | # and it should happen on the same day it was dug 23 | laying_count -= 1 24 | for day_a, day_b in pairwise(dig_days): 25 | sadness_days.append(day_b - day_a) 26 | if laying_count < 0: 27 | return -1 28 | sadness_days.sort() 29 | return sum(sadness_days[: len(sadness_days) - laying_count]) 30 | 31 | 32 | if __name__ == '__main__': 33 | sidewalk_count, dig_count, laying_count = (int(s) for s in input().split()) 34 | digs = [Dig(*map(int, input().split())) for _ in range(dig_count)] 35 | print(min_sadness(digs, sidewalk_count, laying_count)) 36 | -------------------------------------------------------------------------------- /bonus1/l_mnogogosha.py: -------------------------------------------------------------------------------- 1 | def polynomial_hash(s: str, q: int, m: int) -> int: 2 | hash_ = 0 3 | for ch in s: 4 | ch_num = ord(ch) - ord('a') + 1 5 | hash_ = (hash_ * q + ch_num) % m 6 | return hash_ 7 | 8 | 9 | def substrings_k_times(s: str, n: int, k: int) -> list[int]: 10 | # One hash gives too many collisions. Let's use two hashes! 11 | m_1, m_2 = 10**9 + 7, 10**9 + 9 12 | q_1, q_2 = 31, 29 13 | q_1_big = pow(q_1, n - 1, m_1) 14 | q_2_big = pow(q_2, n - 1, m_2) 15 | hash_1 = polynomial_hash(s[:n], q_1, m_1) 16 | hash_2 = polynomial_hash(s[:n], q_2, m_2) 17 | counter = {(hash_1, hash_2): [0, 1]} 18 | for i in range(1, len(s) - n + 1): 19 | ch_prev = ord(s[i - 1]) - ord('a') + 1 20 | ch_next = ord(s[i + n - 1]) - ord('a') + 1 21 | hash_1 = ((hash_1 - ch_prev * q_1_big) * q_1 + ch_next) % m_1 22 | hash_2 = ((hash_2 - ch_prev * q_2_big) * q_2 + ch_next) % m_2 23 | if (hash_1, hash_2) not in counter: 24 | counter[hash_1, hash_2] = [i, 1] 25 | else: 26 | counter[hash_1, hash_2][1] += 1 27 | return [i for i, count in counter.values() if count >= k] 28 | 29 | 30 | if __name__ == '__main__': 31 | n, k = map(int, input().split()) 32 | s = input() 33 | print(*substrings_k_times(s, n, k)) 34 | -------------------------------------------------------------------------------- /bonus1/j_sum_of_four.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | 4 | def find_sum_groups(nums: list[int], target: int) -> list[tuple[int, ...]]: # noqa: C901 5 | nums.sort() 6 | two_sums = defaultdict(list) 7 | result = [] 8 | for i in range(len(nums) - 1): 9 | for j in range(i + 1, len(nums)): 10 | two_sum = nums[i] + nums[j] 11 | two_sums[two_sum].append([i, j]) 12 | for i in range(len(nums) - 1): 13 | if i > 0 and nums[i] == nums[i - 1]: 14 | continue 15 | for j in range(i + 1, len(nums)): 16 | if j > i + 1 and nums[j] == nums[j - 1]: 17 | continue 18 | two_sum = nums[i] + nums[j] 19 | if target - two_sum not in two_sums: 20 | continue 21 | for m, n in two_sums[target - two_sum]: 22 | if m <= j: 23 | continue 24 | group = (nums[i], nums[j], nums[m], nums[n]) 25 | if not result or result[-1] != group: 26 | result.append(group) 27 | return result 28 | 29 | 30 | if __name__ == '__main__': 31 | n = int(input()) 32 | target = int(input()) 33 | a = [int(s) for s in input().split()] 34 | sum_groups = find_sum_groups(a, target) 35 | print(len(sum_groups)) 36 | for sum_group in sum_groups: 37 | print(*sum_group) 38 | -------------------------------------------------------------------------------- /shbr3/b_boomers_and_zoomers.py: -------------------------------------------------------------------------------- 1 | def count_invitations(a: list[int]) -> int: 2 | if len(a) <= 1: 3 | return 0 4 | a = sorted(a) 5 | left, right_first, right = 0, 0, 0 6 | count = 0 7 | # j invites i iff 0.5 * a[j] + 7 < a[i] <= a[j] 8 | while right < len(a): 9 | if a[right] > a[right_first]: 10 | right_first = right 11 | while left < right and 0.5 * a[right] + 7 >= a[left]: 12 | left += 1 13 | if a[left] > 14: 14 | count += (right - left) + (right - right_first) 15 | right += 1 16 | return count 17 | 18 | 19 | def count_invitations_bruteforce(a: list[int]) -> int: 20 | count = 0 21 | for i in range(len(a) - 1): 22 | for j in range(i + 1, len(a)): 23 | if 0.5 * a[i] + 7 < a[j] <= a[i]: 24 | count += 1 25 | if 0.5 * a[j] + 7 < a[i] <= a[j]: 26 | count += 1 27 | return count 28 | 29 | 30 | def test_bruteforce(): 31 | import random 32 | 33 | while True: 34 | a = random.choices(range(1, 121), k=10) 35 | res = count_invitations(a) 36 | res_true = count_invitations_bruteforce(a) 37 | if res != res_true: 38 | print(f'{a=}, {res=}, {res_true=}') 39 | 40 | 41 | if __name__ == '__main__': 42 | _ = input() 43 | ages = [int(s) for s in input().split()] 44 | print(count_invitations(ages)) 45 | -------------------------------------------------------------------------------- /shbr3/e_brewing_potions.py: -------------------------------------------------------------------------------- 1 | from itertools import accumulate 2 | 3 | MAX_QUALITY = 2 * 10**6 4 | 5 | 6 | def check( 7 | min_quality: int, prefix_sums: list[int], quality: list[int], k: int 8 | ) -> int | None: 9 | count, total = 0, 0 10 | for qual in quality: 11 | if qual < min_quality: 12 | break 13 | count += 1 14 | total += qual 15 | j = 1 16 | while j < len(quality) and quality[0] + quality[j] >= min_quality: 17 | j += 1 18 | for i in range(len(quality)): 19 | if i + 1 >= j: 20 | break 21 | while j - 1 > i and quality[i] + quality[j - 1] < min_quality: 22 | j -= 1 23 | cur_count = j - i - 1 24 | count += cur_count 25 | total += prefix_sums[j - 1] - prefix_sums[i] + quality[i] * cur_count 26 | if count >= k: 27 | return total - (count - k) * min_quality 28 | return None 29 | 30 | 31 | def max_sum_quality(quality: list[int], k: int) -> int | None: 32 | quality.sort(reverse=True) 33 | prefix_sums = list(accumulate(quality)) 34 | left, right = -MAX_QUALITY, MAX_QUALITY 35 | while left < right: 36 | mid = (left + right + 1) // 2 37 | if check(mid, prefix_sums, quality, k) is None: 38 | right = mid - 1 39 | else: 40 | left = mid 41 | return check(left, prefix_sums, quality, k) 42 | 43 | 44 | if __name__ == '__main__': 45 | n, k = (int(s) for s in input().split()) 46 | quality = [int(s) for s in input().split()] 47 | print(max_sum_quality(quality, k)) 48 | -------------------------------------------------------------------------------- /sprint3/l_two_bicycles.py: -------------------------------------------------------------------------------- 1 | from bisect import bisect_left 2 | 3 | 4 | def binary_search(a: list[int], k: int) -> int: 5 | left, right = 0, len(a) 6 | while left < right: 7 | mid = (left + right) // 2 8 | if a[mid] == k: 9 | return mid 10 | if a[mid] > k: 11 | right = mid 12 | else: 13 | left = mid + 1 14 | return -1 15 | 16 | 17 | DAY_OFFSET = 1 18 | 19 | 20 | def left_binary_search(a: list[int], k: int) -> int: 21 | left, right = 0, len(a) - 1 22 | while left < right: 23 | mid = (left + right) // 2 24 | if a[mid] >= k: 25 | right = mid 26 | else: 27 | left = mid + 1 28 | return left + DAY_OFFSET if a and a[left] >= k else -1 29 | 30 | 31 | def left_binary_search_rec(a: list[int], k: int) -> int: 32 | def rec(left: int, right: int): 33 | if left > right: 34 | return -1 35 | mid = (left + right) // 2 36 | if a[mid] >= k: 37 | if left == mid: 38 | return mid + DAY_OFFSET 39 | return rec(left, mid) 40 | return rec(mid + 1, right) 41 | 42 | return rec(0, len(a) - 1) 43 | 44 | 45 | def bisect(a: list[int], k: int) -> int: 46 | pos = bisect_left(a, k) 47 | return pos + DAY_OFFSET if pos < len(a) else -1 48 | 49 | 50 | if __name__ == '__main__': 51 | n = int(input()) 52 | a = list(map(int, input().split())) 53 | cost = int(input()) 54 | print( 55 | left_binary_search(a, cost), 56 | left_binary_search(a, 2 * cost), 57 | ) 58 | -------------------------------------------------------------------------------- /sprint3/final_broken_search.py: -------------------------------------------------------------------------------- 1 | """ID посылки: 88287031.""" 2 | 3 | 4 | def broken_search(nums: list[int], target: int) -> int: 5 | """Binary search for value in a shifted sorted list. 6 | 7 | In order to choose which half to move to we need to know 8 | the position of the shift. The shift can be located based 9 | on the values of the left element (a), the middle (m) 10 | and the target (t). 11 | There are six possible arrangements of these three elements, 12 | and for each arrangement their relative positions are enough 13 | to make the choice. 14 | On the diagram of arrangements below the shift is marked as |: 15 | 16 | a--------t--m-----|----- a <= t <= m (choose left half) 17 | a-----------m--t--|----- a <= m <= t (choose right half) 18 | a-----------m-----|--t-- t <= a <= m (choose right half) 19 | a--t--|-----m----------- m <= a <= t (choose left half) 20 | a-----|--t--m----------- t <= m <= a (choose left half) 21 | a-----|-----m--t-------- m <= t <= a (choose right half) 22 | """ 23 | left, right = 0, len(nums) 24 | while left < right: 25 | mid = (left + right) // 2 26 | if nums[mid] == target: 27 | return mid 28 | if ( 29 | nums[mid] <= nums[left] <= target 30 | or target <= nums[mid] <= nums[left] 31 | or nums[left] <= target <= nums[mid] 32 | ): 33 | right = mid 34 | else: 35 | left = mid + 1 36 | return -1 37 | 38 | 39 | if __name__ == '__main__': 40 | _ = int(input()) 41 | target = int(input()) 42 | nums = [int(s) for s in input().split()] 43 | print(broken_search(nums, target)) 44 | -------------------------------------------------------------------------------- /shbr3/c_festive_lights.py: -------------------------------------------------------------------------------- 1 | from itertools import chain, repeat 2 | from math import ceil 3 | 4 | 5 | def make_x_same_lights( 6 | sorted_counts: tuple[int], lights_len: int, x: int 7 | ) -> tuple[int] | None: 8 | used_counts = [] 9 | total = 0 10 | for count in sorted_counts: 11 | used = min(count // x, lights_len - total) 12 | if not used: 13 | break 14 | used_counts.append(used) 15 | total += used 16 | return tuple(used_counts) if total == lights_len else None 17 | 18 | 19 | def count_max_lights( 20 | counts: tuple[int, ...], lights_len: int 21 | ) -> tuple[int, tuple[int, ...]]: 22 | enumerated_counts = sorted( 23 | zip(counts, range(len(counts))), 24 | key=lambda t: (-t[0], t[1]), 25 | ) 26 | sorted_counts, original_indices = zip(*enumerated_counts) 27 | 28 | left, right = 1, sorted_counts[0] 29 | while left <= right: 30 | if left == right: 31 | break 32 | mid = ceil((left + right) / 2) 33 | if make_x_same_lights(sorted_counts, lights_len, mid): 34 | left = mid 35 | else: 36 | right = mid - 1 37 | 38 | lights = make_x_same_lights(sorted_counts, lights_len, left) 39 | if not lights: 40 | return 0, () 41 | return left, tuple( 42 | chain.from_iterable( 43 | repeat(original_indices[index] + 1, count) 44 | for index, count in enumerate(lights) 45 | ) 46 | ) 47 | 48 | 49 | if __name__ == '__main__': 50 | n, k = (int(s) for s in input().split()) 51 | a = tuple(int(input()) for _ in range(k)) 52 | max_count, max_lights = count_max_lights(a, n) 53 | print(max_count) 54 | print(*max_lights, sep='\n') 55 | -------------------------------------------------------------------------------- /bonus2/e_search_tree.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Node: 5 | def __init__( 6 | self, 7 | value, 8 | left: Optional['Node'] = None, 9 | right: Optional['Node'] = None, 10 | ): 11 | self.value = value 12 | self.left = left 13 | self.right = right 14 | 15 | 16 | def traverse(root: Node | None): 17 | """Traverse all nodes in a tree in-order.""" 18 | if not root: 19 | return 20 | yield from traverse(root.left) 21 | yield root 22 | yield from traverse(root.right) 23 | 24 | 25 | def traverse_iter(root: Node | None): 26 | """Traverse all nodes in a tree in-order (without recursion).""" 27 | stack, node = [], root 28 | while stack or node: 29 | if node: 30 | stack.append(node) 31 | node = node.left 32 | else: 33 | node = stack.pop() 34 | yield node 35 | node = node.right 36 | 37 | 38 | def solution(root: Node) -> bool: 39 | """Check if a tree is a search tree.""" 40 | it, it2 = traverse(root), traverse(root) 41 | next(it2, None) 42 | return all(left.value < right.value for left, right in zip(it, it2)) 43 | 44 | 45 | def solution2(root: Node) -> bool: 46 | def rec(root: Node): 47 | is_left_ok, left_min, left_max = ( 48 | rec(root.left) if root.left else (True, root.value, -float('inf')) 49 | ) 50 | is_right_ok, right_min, right_max = ( 51 | rec(root.right) if root.right else (True, float('inf'), root.value) 52 | ) 53 | is_root_ok = ( 54 | is_left_ok and is_right_ok and left_max < root.value < right_min 55 | ) 56 | return is_root_ok, left_min, right_max 57 | 58 | return rec(root)[0] 59 | -------------------------------------------------------------------------------- /sprint3/final_efficient_quicksort.py: -------------------------------------------------------------------------------- 1 | """ID посылки: 88287103.""" 2 | 3 | from dataclasses import dataclass 4 | 5 | 6 | @dataclass 7 | class Participant: 8 | name: str 9 | tasks: int 10 | penalty: int 11 | 12 | @classmethod 13 | def from_text(cls, text: str): 14 | name, tasks, penalty = text.split() 15 | return cls(name=name, tasks=int(tasks), penalty=int(penalty)) 16 | 17 | def __lt__(self, other: 'Participant'): 18 | if not isinstance(other, Participant): 19 | msg = 'Участников можно сравнивать только друг с другом' 20 | raise TypeError(msg) 21 | self_key = (-self.tasks, self.penalty, self.name) 22 | other_key = (-other.tasks, other.penalty, other.name) 23 | return self_key < other_key 24 | 25 | def __str__(self): 26 | return self.name 27 | 28 | 29 | def partition(a: list, left: int, right: int) -> int: 30 | pivot = a[(left + right) // 2] 31 | while True: 32 | while a[left] < pivot: 33 | left += 1 34 | while a[right] > pivot: 35 | right -= 1 36 | if left >= right: 37 | return right 38 | a[left], a[right] = a[right], a[left] 39 | left += 1 40 | right -= 1 41 | 42 | 43 | def quicksort(a: list, left: int = 0, right: int | None = None) -> None: 44 | if right is None: 45 | right = len(a) - 1 46 | if left >= right: 47 | return 48 | border = partition(a, left, right) 49 | quicksort(a, left, border) 50 | quicksort(a, border + 1, right) 51 | 52 | 53 | def main(): 54 | count = int(input()) 55 | participants = [Participant.from_text(input()) for _ in range(count)] 56 | quicksort(participants) 57 | print(*participants, sep='\n') 58 | 59 | 60 | if __name__ == '__main__': 61 | main() 62 | -------------------------------------------------------------------------------- /sprint2/j_list_queue.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class ListQueueDeque: 5 | def __init__(self): 6 | self._queue = deque() 7 | 8 | def put(self, value: int): 9 | self._queue.appendleft(value) 10 | 11 | def get(self): 12 | if not self._queue: 13 | return 'error' 14 | return self._queue.pop() 15 | 16 | def size(self): 17 | return len(self._queue) 18 | 19 | 20 | class Node: 21 | def __init__(self, value, next_item: 'Node | None' = None): 22 | self.value = value 23 | self.next_item = next_item 24 | 25 | 26 | class ListQueue: 27 | def __init__(self): 28 | self.first = self.last = None 29 | self.queue_size = 0 30 | 31 | def put(self, value: int): 32 | node = Node(value) 33 | if not self.first: 34 | self.first = node 35 | else: 36 | self.last.next_item = node 37 | self.last = node 38 | self.queue_size += 1 39 | 40 | def get(self): 41 | if not self.first: 42 | return 'error' 43 | if not self.first.next_item: 44 | self.last = None 45 | value = self.first.value 46 | self.first = self.first.next_item 47 | self.queue_size -= 1 48 | return value 49 | 50 | def size(self): 51 | return self.queue_size 52 | 53 | 54 | def execute(command: str, queue: ListQueue) -> None: 55 | method_name, *args = command.split() 56 | args = list(map(int, args)) 57 | return_value = getattr(queue, method_name)(*args) 58 | if return_value is not None: 59 | print(return_value) 60 | 61 | 62 | if __name__ == '__main__': 63 | n = int(input()) 64 | queue = ListQueue() 65 | for _ in range(n): 66 | command = input() 67 | execute(command, queue) 68 | -------------------------------------------------------------------------------- /shbr4/d_subtree_size.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Iterable 2 | 3 | 4 | class Node: 5 | def __init__(self, value): 6 | self.value = value 7 | self.children = [] 8 | 9 | 10 | Nodes = list[Node] 11 | 12 | 13 | def traverse_preorder(node: Node) -> Iterable[Node]: 14 | stack = [node] 15 | while stack: 16 | node = stack.pop() 17 | yield node 18 | stack.extend(reversed(node.children)) 19 | 20 | 21 | def traverse_postorder(node: Node) -> Iterable[Node]: 22 | stack = [node] 23 | visited = set() 24 | while stack: 25 | node = stack.pop() 26 | if node in visited: 27 | yield node 28 | continue 29 | stack.append(node) 30 | stack.extend(reversed(node.children)) 31 | visited.add(node) 32 | 33 | 34 | def create_directed_tree(vertice_count: int, edges: list[list[int]]) -> Nodes: 35 | nodes = [Node(key) for key in range(1, vertice_count + 1)] 36 | for left, right in edges: 37 | left_node, right_node = nodes[left - 1], nodes[right - 1] 38 | left_node.children.append(right_node) 39 | right_node.children.append(left_node) 40 | for node in traverse_preorder(nodes[0]): 41 | for child in node.children: 42 | child.children.remove(node) 43 | return nodes 44 | 45 | 46 | def subtree_sizes(nodes: Nodes) -> list[int]: 47 | sizes = [0] * len(nodes) 48 | for node in traverse_postorder(nodes[0]): 49 | sizes[node.value - 1] = 1 + sum( 50 | sizes[child.value - 1] for child in node.children 51 | ) 52 | return sizes 53 | 54 | 55 | if __name__ == '__main__': 56 | vertice_count = int(input()) 57 | edges = [ 58 | [int(s) for s in input().split()] for _ in range(vertice_count - 1) 59 | ] 60 | nodes = create_directed_tree(vertice_count, edges) 61 | print(*subtree_sizes(nodes), sep=' ') 62 | -------------------------------------------------------------------------------- /bonus1/i_common_subarray.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from functools import reduce 3 | 4 | m, q = 10**9 + 7, 257 5 | 6 | 7 | def prefix_hash(a: list[int], n: int) -> int: 8 | return reduce(lambda acc, i: (acc * q + a[i] + 1) % m, range(n), 0) 9 | 10 | 11 | def is_not_collision( 12 | a: list[int], b: list[int], n: int, a_indices: list[int], b_index: int 13 | ) -> bool: 14 | return any( 15 | all(a[a_index + i] == b[b_index + i] for i in range(n)) 16 | for a_index in a_indices 17 | ) 18 | 19 | 20 | def has_common_subarray_of_len(a: list[int], b: list[int], n: int) -> bool: 21 | q_big = pow(q, n - 1, m) 22 | 23 | a_hashes: defaultdict[int, list[int]] = defaultdict(list) 24 | h = prefix_hash(a, n) 25 | a_hashes[h].append(0) 26 | for i in range(1, len(a) - n + 1): 27 | h = ((h - (a[i - 1] + 1) * q_big) * q + a[i + n - 1] + 1) % m 28 | a_hashes[h].append(i) 29 | 30 | h = prefix_hash(b, n) 31 | if h in a_hashes and is_not_collision(a, b, n, a_hashes[h], 0): 32 | return True 33 | for i in range(1, len(b) - n + 1): 34 | h = ((h - (b[i - 1] + 1) * q_big) * q + b[i + n - 1] + 1) % m 35 | if h in a_hashes and is_not_collision(a, b, n, a_hashes[h], i): 36 | return True 37 | 38 | return False 39 | 40 | 41 | def longest_common_subarray_len(a: list[int], b: list[int]) -> int: 42 | if len(a) > len(b): 43 | a, b = b, a 44 | 45 | left, right = 0, len(a) 46 | while left < right: 47 | mid = (left + right + 1) // 2 48 | if has_common_subarray_of_len(a, b, mid): 49 | left = mid 50 | else: 51 | right = mid - 1 52 | return left 53 | 54 | 55 | if __name__ == '__main__': 56 | n = int(input()) 57 | a = [int(s) for s in input().split()] 58 | m = int(input()) 59 | b = [int(s) for s in input().split()] 60 | print(longest_common_subarray_len(a, b)) 61 | -------------------------------------------------------------------------------- /shbr2/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from shbr2.a_repeating_number import has_repeats_at_dist 4 | from shbr2.b_multicolor_sticks import count_sticks 5 | from shbr2.c_replacing_words import replace_words 6 | from shbr2.d_majority import majority 7 | from shbr2.e_deleting_numbers import min_to_delete 8 | 9 | 10 | @pytest.mark.parametrize( 11 | 'a, max_dist, expected', 12 | ( 13 | ([1, 2, 3, 1], 2, False), 14 | ([1, 0, 1, 1], 1, True), 15 | ([1, 2, 3, 1, 2, 3], 2, False), 16 | ), 17 | ) 18 | def test_a(a, max_dist, expected): 19 | assert has_repeats_at_dist(a, max_dist) == expected 20 | 21 | 22 | @pytest.mark.parametrize( 23 | 'test_input, expected', 24 | ( 25 | ('R2G1R1B2G2', 1), 26 | ('R9G1B0', 0), 27 | ('R0G0B0R0G0B0R0G0B0', 1), 28 | ), 29 | ) 30 | def test_b(test_input, expected): 31 | assert count_sticks(test_input) == expected 32 | 33 | 34 | @pytest.mark.parametrize( 35 | 'replacements, words, expected', 36 | ( 37 | ( 38 | ['a', 'b'], 39 | ['abdafb', 'basrt', 'casds', 'dsasa', 'a'], 40 | ['a', 'b', 'casds', 'dsasa', 'a'], 41 | ), 42 | ( 43 | ['aa', 'bc', 'aaa'], 44 | ['a', 'aa', 'aaa', 'bcd', 'abcd'], 45 | ['a', 'aa', 'aa', 'bc', 'abcd'], 46 | ), 47 | ), 48 | ) 49 | def test_c(replacements, words, expected): 50 | assert list(replace_words(words, replacements)) == expected 51 | 52 | 53 | @pytest.mark.parametrize( 54 | 'test_input, expected', 55 | ( 56 | ([1, 2, 1], 1), 57 | ([7, 5, 5, 5, 5, 4, 5], 5), 58 | ([3, 3, 3, 1], 3), 59 | ), 60 | ) 61 | def test_d(test_input, expected): 62 | assert majority(test_input) == expected 63 | 64 | 65 | @pytest.mark.parametrize( 66 | 'test_input, expected', 67 | ( 68 | ([1, 2, 3, 4, 5], 3), 69 | ([1, 1, 2, 3, 5, 5, 2, 2, 1, 5], 4), 70 | ([1, 3, 5], 2), 71 | ([1], 0), 72 | ([1, 2], 0), 73 | ([1, 1, 1], 0), 74 | ), 75 | ) 76 | def test_e(test_input, expected): 77 | assert min_to_delete(test_input) == expected 78 | -------------------------------------------------------------------------------- /sprint2/final_deque.py: -------------------------------------------------------------------------------- 1 | """ID посылки: 87911435.""" 2 | 3 | 4 | class DequeFullError(Exception): 5 | pass 6 | 7 | 8 | class DequeEmptyError(Exception): 9 | pass 10 | 11 | 12 | class Deque: 13 | def __init__(self, max_size: int): 14 | self._max_size = max_size 15 | self._deque: list[int | None] = [None] * max_size 16 | self._first = 1 17 | self._last = 0 18 | self._size = 0 19 | 20 | def _check_full(self): 21 | if self._size == self._max_size: 22 | msg = 'Max deque size reached' 23 | raise DequeFullError(msg) 24 | 25 | def _check_empty(self): 26 | if self._size == 0: 27 | msg = 'Deque is empty' 28 | raise DequeEmptyError(msg) 29 | 30 | def push_front(self, value: int): 31 | self._check_full() 32 | self._first = (self._first - 1) % self._max_size 33 | self._deque[self._first] = value 34 | self._size += 1 35 | 36 | def push_back(self, value: int): 37 | self._check_full() 38 | self._last = (self._last + 1) % self._max_size 39 | self._deque[self._last] = value 40 | self._size += 1 41 | 42 | def pop_front(self): 43 | self._check_empty() 44 | value = self._deque[self._first] 45 | self._first = (self._first + 1) % self._max_size 46 | self._size -= 1 47 | return value 48 | 49 | def pop_back(self): 50 | self._check_empty() 51 | value = self._deque[self._last] 52 | self._last = (self._last - 1) % self._max_size 53 | self._size -= 1 54 | return value 55 | 56 | 57 | def main(): 58 | command_count = int(input()) 59 | max_size = int(input()) 60 | deque = Deque(max_size) 61 | for _ in range(command_count): 62 | method_name, *method_args = input().split() 63 | method_args = map(int, method_args) 64 | try: 65 | return_value = getattr(deque, method_name)(*method_args) 66 | if return_value is not None: 67 | print(return_value) 68 | except (DequeFullError, DequeEmptyError): 69 | print('error') 70 | 71 | 72 | if __name__ == '__main__': 73 | main() 74 | -------------------------------------------------------------------------------- /sprint2/i_limited_queue.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class MyQueueSizedDeque: 5 | def __init__(self, max_size: int): 6 | self.max_size = max_size 7 | self._queue = deque() 8 | 9 | def push(self, value: int): 10 | if self.size() >= self.max_size: 11 | return 'error' 12 | self._queue.appendleft(value) 13 | return None 14 | 15 | def pop(self): 16 | if not self._queue: 17 | return 'None' 18 | return self._queue.pop() 19 | 20 | def peek(self): 21 | if not self._queue: 22 | return 'None' 23 | return self._queue[-1] 24 | 25 | def size(self): 26 | return len(self._queue) 27 | 28 | 29 | class MyQueueSized: 30 | def __init__(self, max_size: int): 31 | self.max_size = max_size 32 | self.queue: list[int | None] = [None] * max_size 33 | self.index_first = self.index_last = 0 34 | self.queue_size = 0 35 | 36 | def push(self, value: int): 37 | if self.queue_size >= self.max_size: 38 | return 'error' 39 | self.queue[self.index_last] = value 40 | self.index_last = (self.index_last + 1) % self.max_size 41 | self.queue_size += 1 42 | return None 43 | 44 | def pop(self): 45 | if not self.queue_size: 46 | return 'None' 47 | value = self.queue[self.index_first] 48 | self.index_first = (self.index_first + 1) % self.max_size 49 | self.queue_size -= 1 50 | return value 51 | 52 | def peek(self): 53 | if not self.queue_size: 54 | return 'None' 55 | return self.queue[self.index_first] 56 | 57 | def size(self): 58 | return self.queue_size 59 | 60 | 61 | def execute(command: str, queue: MyQueueSized) -> None: 62 | method_name, *args = command.split() 63 | args = list(map(int, args)) 64 | return_value = getattr(queue, method_name)(*args) 65 | if return_value is not None: 66 | print(return_value) 67 | 68 | 69 | if __name__ == '__main__': 70 | n = int(input()) 71 | max_size = int(input()) 72 | queue = MyQueueSized(max_size) 73 | for _ in range(n): 74 | command = input() 75 | execute(command, queue) 76 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "algorithms" 3 | version = "0.1.0" 4 | description = "Add your description here" 5 | readme = "README.md" 6 | requires-python = ">=3.13" 7 | dependencies = [] 8 | 9 | [dependency-groups] 10 | dev = [ 11 | "pytest>=8.3.4", 12 | ] 13 | 14 | [tool.ruff] 15 | target-version = "py313" 16 | line-length = 79 17 | preview = true 18 | output-format = "concise" # preview mode switches this to full 19 | 20 | [tool.ruff.lint] 21 | select = ["ALL"] 22 | ignore = [ 23 | "D1", # pydocstyle (allow missing docstrings) 24 | "ANN", # flake8-annotations (demands type annotations) 25 | "S", # flake8-bandit (security testing) 26 | "COM", # flake8-commas (conflicts with formatter) 27 | "CPY", # flake8-copyright (demands copyright notices) 28 | "Q", # flake8-quotes (conflicts with formatter) 29 | "T20", # flake8-print (prohibits print statements) 30 | "ISC001", # single-line-implicit-string-concatenation (conflicts with formatter) 31 | "PGH003", # blanket-type-ignore (PyLance doesn't provide error codes) 32 | "B905", # zip-without-explicit-strict (makes zip too bulky) 33 | "E731", # lambda-assignment (precludes a concise functional style) 34 | "PLC0415", # import-outside-top-level (sometimes imports in a function are necessary) 35 | "PLR2004", # magic-value-comparison (demands too many constants) 36 | "TD003", # missing-todo-link (too cumbersome) 37 | "G004", # logging-f-string (pointless micro-optimization in most cases) 38 | "DOC201", # docstring-missing-returns (excessive documentation) 39 | "DOC402", # docstring-missing-yields (excessive documentation) 40 | ] 41 | allowed-confusables = [ 42 | "а", "б", "в", "г", "е", "з", "и", "к", "м", "н", "о", "р", "с", "у", "ф", "х", 43 | "А", "Б", "В", "Г", "Е", "З", "И", "К", "М", "Н", "О", "Р", "С", "У", "Ф", "Х", 44 | ] 45 | 46 | [tool.ruff.lint.per-file-ignores] 47 | "tests/*.py" = ["N802"] 48 | 49 | [tool.ruff.lint.pydocstyle] 50 | convention = "pep257" 51 | 52 | [tool.ruff.lint.flake8-pytest-style] 53 | fixture-parentheses = false 54 | parametrize-names-type = "csv" 55 | parametrize-values-type = "tuple" 56 | 57 | [tool.ruff.format] 58 | quote-style = "single" 59 | -------------------------------------------------------------------------------- /yaintern/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | from itertools import starmap 2 | 3 | import pytest 4 | 5 | from yaintern.a_usb import min_price 6 | from yaintern.b_even_fence import min_minmax_diff 7 | from yaintern.c_repo_inheritance import make_tree, max_depth 8 | from yaintern.d_digs_and_tiles import Dig, min_sadness 9 | from yaintern.e_priority_admission import enroll 10 | 11 | 12 | @pytest.mark.parametrize( 13 | 'test_input, expected', 14 | ( 15 | ((1, 3, 1, 10), 2), 16 | ((2, 4, 9, 10), 10), 17 | ((3, 8, 9, 10), 19), 18 | ), 19 | ) 20 | def test_a(test_input, expected): 21 | assert min_price(*test_input) == expected 22 | 23 | 24 | @pytest.mark.parametrize( 25 | 'test_input, expected', 26 | ( 27 | (([15, 19], 0), 4), 28 | (([1, 11, 6, 41, 15, 13, 14], 2), 9), 29 | ), 30 | ) 31 | def test_b(test_input, expected): 32 | assert min_minmax_diff(*test_input) == expected 33 | 34 | 35 | @pytest.mark.parametrize( 36 | 'test_input, expected', 37 | ( 38 | ((0, 0, 1), 3), 39 | ((0, 1, 2, 0, 4, 5, 6, 4), 7), 40 | ), 41 | ) 42 | def test_c(test_input, expected): 43 | leaves = make_tree(test_input) 44 | assert max_depth(leaves) == expected 45 | 46 | 47 | @pytest.mark.parametrize( 48 | 'digs, sidewalk_count, laying_count, expected', 49 | ( 50 | ( 51 | ( 52 | (1, 2), 53 | (2, 3), 54 | (2, 1), 55 | (6, 2), 56 | ), 57 | 5, 58 | 3, 59 | 5, 60 | ), 61 | ( 62 | ( 63 | (1, 1), 64 | (1, 2), 65 | (3, 1), 66 | (4, 2), 67 | ), 68 | 2, 69 | 2, 70 | 5, 71 | ), 72 | ), 73 | ) 74 | def test_d(digs, sidewalk_count, laying_count, expected): 75 | digs = list(starmap(Dig, digs)) 76 | assert min_sadness(digs, sidewalk_count, laying_count) == expected 77 | 78 | 79 | @pytest.mark.parametrize( 80 | 'program_spots, priorities, expected', 81 | ( 82 | ( 83 | [1, 5], 84 | ( 85 | (3, 1, 1), 86 | (1, 1, 2), 87 | (2, 2, 1, 2), 88 | (3, 2, 1, 2), 89 | ), 90 | [-1, 2, 1, 2], 91 | ), 92 | ), 93 | ) 94 | def test_e(program_spots, priorities, expected): 95 | assert enroll(program_spots, priorities) == expected 96 | -------------------------------------------------------------------------------- /shbr5/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from shbr5.a_bulls_and_cows import count_bulls_and_cows 4 | from shbr5.b_largest_substring import find_max_good_substring_length 5 | from shbr5.c_mountains import find_peak 6 | from shbr5.d_delete_string import min_length_after_ops 7 | from shbr5.e_pitcraft import count_filled_blocks 8 | 9 | 10 | @pytest.mark.parametrize( 11 | 'test_input, expected', 12 | ((('1370', '7311'), (1, 2)),), 13 | ) 14 | def test_a(test_input, expected): 15 | assert count_bulls_and_cows(*test_input) == expected 16 | 17 | 18 | @pytest.mark.parametrize( 19 | 's, min_count, expected', 20 | ( 21 | ('aaabb', 3, 3), 22 | ('ababbc', 2, 5), 23 | ('aaabb', 4, 0), 24 | ('aaabbb', 0, 6), 25 | ('aaabbb', 1, 6), 26 | ('aaabbb', 2, 6), 27 | ('aaabbb', 3, 6), 28 | ('aaabbb', 4, 0), 29 | ('adbacbacaaacccb', 2, 13), 30 | ('adbacbacaaacccb', 4, 8), 31 | ('abacc', 2, 2), 32 | ), 33 | ) 34 | def test_b(s, min_count, expected): 35 | assert find_max_good_substring_length(s, min_count) == expected 36 | 37 | 38 | @pytest.mark.parametrize( 39 | 'test_input, expected', 40 | ( 41 | ([1, 2, 1], 2), 42 | ([1, 2, 3, 1], 3), 43 | ([1, 2, 3, 4, 5, 4, 3, 2, 1], 5), 44 | ([1, 2, 3, 4, 5, 1], 5), 45 | ([1, 5, 4, 3, 2, 1], 2), 46 | ), 47 | ) 48 | def test_c(test_input, expected): 49 | assert find_peak(test_input) == expected 50 | 51 | 52 | @pytest.mark.parametrize( 53 | 'test_input, expected', 54 | ( 55 | ('abba', 0), 56 | ('aboba', 1), 57 | ('zzzaabxxxcazz', 5), 58 | ), 59 | ) 60 | def test_d(test_input, expected): 61 | assert min_length_after_ops(test_input) == expected 62 | 63 | 64 | sample_filetree = [ 65 | 'emoh', 66 | ' vonavi', 67 | ' a.doc', 68 | ' b.doc', 69 | ' vortep', 70 | ' .bashrc', 71 | ' vorodis', 72 | ' onrop', 73 | ' 1.avi', 74 | ' 2.avi', 75 | 'top.doc', 76 | 'rav', 77 | ' bil', 78 | ] 79 | 80 | 81 | @pytest.mark.parametrize( 82 | 'test_input, expected', 83 | ( 84 | ( 85 | [2, 5, 2, 3, 6, 9, 3, 1, 3, 4, 6], 86 | 18, 87 | ), 88 | ( 89 | [1, 4, 3, 1, 5, 1, 7, 5, 2, 3, 6, 7, 10, 3, 7, 13, 5, 3, 8, 6, 4], 90 | 38, 91 | ), 92 | ), 93 | ) 94 | def test_e(test_input, expected): 95 | assert count_filled_blocks(test_input) == expected 96 | -------------------------------------------------------------------------------- /shbr1/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from shbr1.a_steps import steps 4 | from shbr1.b_canonical_path import get_canonical_path 5 | from shbr1.c_buy_sell import find_max_difference 6 | from shbr1.d_time_difference import min_gap 7 | from shbr1.e_break_palindrome import break_palindrome, break_palindrome_func 8 | 9 | 10 | @pytest.mark.parametrize( 11 | 'test_input, expected', 12 | ( 13 | (1, 1), 14 | (2, 1), 15 | (3, 2), 16 | (4, 2), 17 | (5, 2), 18 | (6, 3), 19 | (7, 3), 20 | (8, 3), 21 | ), 22 | ) 23 | def test_a(test_input, expected): 24 | assert steps(test_input) == expected 25 | 26 | 27 | @pytest.mark.parametrize( 28 | 'test_input, expected', 29 | ( 30 | ('/home/', '/home'), 31 | ('/../', '/'), 32 | ('/home//foo/', '/home/foo'), 33 | ), 34 | ) 35 | def test_b(test_input, expected): 36 | assert get_canonical_path(test_input) == expected 37 | 38 | 39 | @pytest.mark.parametrize( 40 | 'test_input, expected', 41 | ( 42 | ([10, 3, 5, 3, 11, 9], (2, 5)), 43 | ([5, 5, 5, 5], (0, 0)), 44 | ([1], (0, 0)), 45 | ([4, 3, 2, 1], (0, 0)), 46 | ([9, 10, 1, 5], (3, 4)), 47 | ([1, 10, 2, 5], (1, 2)), 48 | ([10, 2, 9, 1], (2, 3)), 49 | ([9, 7, 4, 5, 6, 3, 1], (3, 5)), 50 | ([9, 7, 4, 5, 6, 3, 1, 2], (7, 8)), 51 | ([1001, 2000], (1, 2)), 52 | ([6, 9, 8, 3, 4], (1, 2)), 53 | ([9, 4, 8, 3, 6], (2, 3)), 54 | ([3, 5, 8, 1, 5], (4, 5)), 55 | ), 56 | ) 57 | def test_c(test_input, expected): 58 | assert find_max_difference(test_input) == expected 59 | 60 | 61 | @pytest.mark.parametrize( 62 | 'test_input, expected', 63 | ( 64 | (['23:59', '00:00'], 1), 65 | (['00:00', '23:59', '00:00'], 0), 66 | (['23:30', '00:30'], 60), 67 | (['14:40', '14:51'], 11), 68 | (['14:40', '15:51', '20:00'], 71), 69 | ), 70 | ) 71 | def test_d(test_input, expected): 72 | assert min_gap(test_input) == expected 73 | 74 | 75 | @pytest.mark.parametrize('func', (break_palindrome, break_palindrome_func)) 76 | @pytest.mark.parametrize( 77 | 'test_input, expected', 78 | ( 79 | ('abba', 'aaba'), 80 | ('a', ''), 81 | ('bbb', 'abb'), 82 | ('cadac', 'aadac'), 83 | ('', ''), 84 | ('aaaa', 'aaab'), 85 | ('aba', 'abb'), 86 | ('aabaa', 'aabab'), 87 | ('bbbabbb', 'abbabbb'), 88 | ), 89 | ) 90 | def test_e(func, test_input, expected): 91 | assert func(test_input) == expected 92 | -------------------------------------------------------------------------------- /bonus1/k_nearest_stop.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from itertools import product 3 | 4 | Coord = tuple[int, int] 5 | Coords = list[Coord] 6 | 7 | 8 | def get_segment(c: Coord) -> Coord: 9 | return (c[0] // 5, c[1] // 5) 10 | 11 | 12 | def subway_with_most_stops(subways: Coords, stops: Coords) -> int: 13 | stops_by_segment = {} 14 | for stop in stops: 15 | segment = get_segment(stop) 16 | if segment in stops_by_segment: 17 | stops_by_segment[segment][stop] += 1 18 | else: 19 | stops_by_segment[segment] = Counter([stop]) 20 | max_count, max_subway_index = 0, 0 21 | for index, subway in enumerate(subways): 22 | seg_x, seg_y = get_segment(subway) 23 | count = 0 24 | # Check 81 segments in 50x50 area around the subway segment 25 | for dx, dy in product(range(-4, 5), repeat=2): 26 | segment = (seg_x + dx, seg_y + dy) 27 | if segment not in stops_by_segment: 28 | continue 29 | # Innermost segments are guaranteed to be in the circle 30 | if ( 31 | -2 <= dx <= 2 32 | and -2 <= dy <= 2 33 | and not ((dx in {-2, 2}) and (dy in {-2, 2})) 34 | ): 35 | count += sum(stops_by_segment[segment].values()) 36 | continue 37 | for stop, stop_count in stops_by_segment[segment].items(): 38 | if ( 39 | (subway[0] - stop[0]) ** 2 + (subway[1] - stop[1]) ** 2 40 | ) <= 400: 41 | count += stop_count 42 | if count > max_count: 43 | max_count = count 44 | max_subway_index = index 45 | return max_subway_index + 1 46 | 47 | 48 | def main(): 49 | n = int(input()) 50 | subways = [tuple(int(s) for s in input().split()) for _ in range(n)] 51 | m = int(input()) 52 | stops = [tuple(int(s) for s in input().split()) for _ in range(m)] 53 | print(subway_with_most_stops(subways, stops)) # type: ignore 54 | 55 | 56 | def reverse_engineer_killer_sequence(subways: Coords, stops: Coords): 57 | print(f'Subway count: {len(subways)}, unique: {len(set(subways))}') 58 | min_, max_ = min(subways), max(subways) 59 | spread = (max_[0] - min_[0], max_[1] - min_[1]) 60 | print(f'min: {min_}, max: {max_}, spread: {spread})') 61 | 62 | print(f'Stop count: {len(stops)}, unique: {len(set(stops))}') 63 | min_, max_ = min(stops), max(stops) 64 | spread = (max_[0] - min_[0], max_[1] - min_[1]) 65 | print(f'min: {min_}, max: {max_}, spread: {spread})') 66 | 67 | 68 | def test_killer_sequence(): 69 | from random import randint, seed 70 | from time import time 71 | 72 | seed(1) 73 | s = 165 74 | c1 = randint(-(10**9), 10**9) 75 | c2 = randint(-(10**9), 10**9) 76 | n = 10**4 77 | subways = [(c1 + randint(-s, s), c2 + randint(-s, s)) for _ in range(n)] 78 | m = 10**5 79 | stops = [(c1 + randint(-s, s), c2 + randint(-s, s)) for _ in range(m)] 80 | 81 | now = time() 82 | print(subway_with_most_stops(subways, stops)) 83 | print(f'{time() - now:.3f} sec') 84 | 85 | 86 | if __name__ == '__main__': 87 | main() 88 | -------------------------------------------------------------------------------- /shbr4/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from shbr4.a_groups_and_rooms import max_groups 4 | from shbr4.b_guests import find_dates 5 | from shbr4.c_socks import thickness 6 | from shbr4.d_subtree_size import create_directed_tree, subtree_sizes 7 | from shbr4.e_filepath import find_path 8 | 9 | 10 | @pytest.mark.parametrize( 11 | 'groups, rooms, expected', 12 | ( 13 | ([3, 1, 2], [1, 1], 1), 14 | ([1, 2], [3, 2, 1], 2), 15 | ([2, 2, 2], [0, 0], 0), 16 | ([2, 2, 2], [2, 2, 2], 3), 17 | ([2, 2, 2], [4, 4, 4, 4], 3), 18 | ([2, 2, 2], [4, 4], 2), 19 | ), 20 | ) 21 | def test_a(groups, rooms, expected): 22 | assert max_groups(groups, rooms) == expected 23 | 24 | 25 | @pytest.mark.parametrize( 26 | 'test_input, expected', 27 | ( 28 | ([[1, 2], [2, 4], [3, 5]], [[1, 2], [3, 4], [5, 5]]), 29 | ([[2, 3], [1, 4], [3, 5]], [[-1, -1], [1, 4], [5, 5]]), 30 | ), 31 | ) 32 | def test_b(test_input, expected): 33 | assert find_dates(test_input) == expected 34 | 35 | 36 | @pytest.mark.parametrize( 37 | 'socks, points, expected', 38 | ( 39 | ( 40 | [ 41 | [6, 11], 42 | [10, 15], 43 | [3, 18], 44 | [1, 19], 45 | [10, 17], 46 | [1, 10], 47 | [6, 16], 48 | [20, 21], 49 | [1, 1], 50 | [12, 21], 51 | [5, 9], 52 | [1, 10], 53 | [5, 10], 54 | [6, 11], 55 | [5, 6], 56 | [7, 11], 57 | [1, 19], 58 | [13, 15], 59 | ], 60 | [5, 22, 19, 3, 8, 16, 16, 21], 61 | [8, 0, 3, 5, 11, 6, 6, 2], 62 | ), 63 | ), 64 | ) 65 | def test_c(socks, points, expected): 66 | assert thickness(socks, points) == expected 67 | 68 | 69 | @pytest.mark.parametrize( 70 | 'vertice_count, edges, expected', 71 | ( 72 | ( 73 | 4, 74 | [[1, 2], [1, 3], [1, 4]], 75 | [4, 1, 1, 1], 76 | ), 77 | ( 78 | 7, 79 | [[1, 2], [1, 3], [1, 4], [5, 1], [5, 6], [5, 7]], 80 | [7, 1, 1, 1, 3, 1, 1], 81 | ), 82 | ), 83 | ) 84 | def test_d(vertice_count, edges, expected): 85 | nodes = create_directed_tree(vertice_count, edges) 86 | assert subtree_sizes(nodes) == expected 87 | 88 | 89 | sample_filetree = [ 90 | 'emoh', 91 | ' vonavi', 92 | ' a.doc', 93 | ' b.doc', 94 | ' vortep', 95 | ' .bashrc', 96 | ' vorodis', 97 | ' onrop', 98 | ' 1.avi', 99 | ' 2.avi', 100 | 'top.doc', 101 | 'rav', 102 | ' bil', 103 | ] 104 | 105 | 106 | @pytest.mark.parametrize( 107 | 'filetree, filename, expected', 108 | ( 109 | (sample_filetree, '1.avi', '/emoh/vorodis/onrop/1.avi'), 110 | (sample_filetree, '2.avi', '/emoh/vorodis/onrop/2.avi'), 111 | (sample_filetree, 'a.doc', '/emoh/vonavi/a.doc'), 112 | (sample_filetree, 'b.doc', '/emoh/vonavi/b.doc'), 113 | (sample_filetree, '.bashrc', '/emoh/vortep/.bashrc'), 114 | (sample_filetree, 'top.doc', '/top.doc'), 115 | ), 116 | ) 117 | def test_e(filetree, filename, expected): 118 | assert find_path(filetree, filename) == expected 119 | -------------------------------------------------------------------------------- /sprint1/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | from sprint1.a_function_values import func 2 | from sprint1.b_even_odd import check_parity 3 | from sprint1.c_neighbors import neighbors 4 | from sprint1.d_weather import calc_chaos 5 | from sprint1.e_longest_word import find_longest_word 6 | from sprint1.f_palindrome import is_palindrome 7 | from sprint1.final_nearest_zero import nearest_zeroes 8 | from sprint1.final_sleight_of_hand import max_score 9 | from sprint1.g_working_from_home import to_binary 10 | from sprint1.h_binary import binary_sum 11 | from sprint1.i_power_of_four import is_power_of_four 12 | from sprint1.j_factorization import factorization 13 | from sprint1.k_list_form import special_sum 14 | from sprint1.l_extra_letter import extra_letter 15 | 16 | 17 | def test_a(): 18 | assert func(-8, -5, -2, 7) == -183 19 | assert func(8, 2, 9, -10) == 40 20 | 21 | 22 | def test_b(): 23 | assert check_parity(1, 2, -3) == 'FAIL' 24 | assert check_parity(7, 11, 7) == 'WIN' 25 | assert check_parity(6, -2, 0) == 'WIN' 26 | 27 | 28 | def test_c(): 29 | matrix = [ 30 | [1, 2, 3], 31 | [0, 2, 6], 32 | [7, 4, 1], 33 | [2, 7, 0], 34 | ] 35 | assert neighbors(matrix, 3, 0) == [7, 7] 36 | assert neighbors(matrix, 0, 0) == [0, 2] 37 | 38 | 39 | def test_d(): 40 | assert calc_chaos([-1, -10, -8, 0, 2, 0, 5]) == 3 41 | assert calc_chaos([1, 2, 5, 4, 8]) == 2 42 | 43 | 44 | def test_e(): 45 | assert find_longest_word('i love segment tree') == 'segment' 46 | assert find_longest_word('frog jumps from river') == 'jumps' 47 | 48 | 49 | def test_f(): 50 | assert is_palindrome('A man, a plan, a canal: Panama') 51 | assert not is_palindrome('A man') 52 | 53 | 54 | def test_g(): 55 | assert to_binary(0) == '0' 56 | assert to_binary(5) == '101' 57 | assert to_binary(16) == '10000' 58 | 59 | 60 | def test_h(): 61 | assert binary_sum('1010', '1011') == '10101' 62 | assert binary_sum('111', '10') == '1001' 63 | assert binary_sum('1', '1000') == '1001' 64 | assert binary_sum('1', '1') == '10' 65 | assert binary_sum('1', '0') == '1' 66 | assert binary_sum('0', '0') == '0' 67 | 68 | 69 | def test_i(): 70 | assert not is_power_of_four(15) 71 | assert not is_power_of_four(257) 72 | assert is_power_of_four(4) 73 | assert is_power_of_four(16) 74 | assert is_power_of_four(256) 75 | 76 | 77 | def test_j(): 78 | assert list(factorization(8)) == [2, 2, 2] 79 | assert list(factorization(2 * 3 * 3 * 5 * 11)) == [2, 3, 3, 5, 11] 80 | assert list(factorization(1)) == [1] 81 | 82 | 83 | def test_k(): 84 | assert special_sum([1, 2, 0, 0], 34) == [1, 2, 3, 4] 85 | assert special_sum([9, 5], 17) == [1, 1, 2] 86 | 87 | 88 | def test_l(): 89 | assert extra_letter('abcd', 'abcde') == 'e' 90 | assert extra_letter('abc', 'abcb') == 'b' 91 | 92 | 93 | def test_final_nearest_zeroes(): 94 | assert nearest_zeroes([0, 1, 4, 9, 0]) == [0, 1, 2, 1, 0] 95 | assert nearest_zeroes([0, 1, 2, 3, 4, 0]) == [0, 1, 2, 2, 1, 0] 96 | assert nearest_zeroes([1, 2, 3, 0, 4, 5, 0, 6]) == [3, 2, 1, 0, 1, 1, 0, 1] 97 | assert nearest_zeroes([1, 0, 0, 2]) == [1, 0, 0, 1] 98 | assert nearest_zeroes([1, 0, 0, 2, 3]) == [1, 0, 0, 1, 2] 99 | 100 | 101 | def test_final_sleight_of_hand(): 102 | assert max_score(['1231', '2..2', '2..2', '2..2'], 3) == 2 103 | assert max_score(['1111', '9999', '1111', '9911'], 4) == 1 104 | assert max_score(['1111', '1111', '1111', '1111'], 4) == 0 105 | assert max_score(['1999', '5436', '4368', '1712'], 1) == 7 106 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.13" 3 | 4 | [[package]] 5 | name = "algorithms" 6 | version = "0.1.0" 7 | source = { virtual = "." } 8 | 9 | [package.dev-dependencies] 10 | dev = [ 11 | { name = "pytest" }, 12 | ] 13 | 14 | [package.metadata] 15 | 16 | [package.metadata.requires-dev] 17 | dev = [{ name = "pytest", specifier = ">=8.3.4" }] 18 | 19 | [[package]] 20 | name = "colorama" 21 | version = "0.4.6" 22 | source = { registry = "https://pypi.org/simple" } 23 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 24 | wheels = [ 25 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 26 | ] 27 | 28 | [[package]] 29 | name = "iniconfig" 30 | version = "2.0.0" 31 | source = { registry = "https://pypi.org/simple" } 32 | sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } 33 | wheels = [ 34 | { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, 35 | ] 36 | 37 | [[package]] 38 | name = "packaging" 39 | version = "24.2" 40 | source = { registry = "https://pypi.org/simple" } 41 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } 42 | wheels = [ 43 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, 44 | ] 45 | 46 | [[package]] 47 | name = "pluggy" 48 | version = "1.5.0" 49 | source = { registry = "https://pypi.org/simple" } 50 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } 51 | wheels = [ 52 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, 53 | ] 54 | 55 | [[package]] 56 | name = "pytest" 57 | version = "8.3.4" 58 | source = { registry = "https://pypi.org/simple" } 59 | dependencies = [ 60 | { name = "colorama", marker = "sys_platform == 'win32'" }, 61 | { name = "iniconfig" }, 62 | { name = "packaging" }, 63 | { name = "pluggy" }, 64 | ] 65 | sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } 66 | wheels = [ 67 | { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, 68 | ] 69 | -------------------------------------------------------------------------------- /shbr3/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | from io import StringIO 2 | 3 | import pytest 4 | 5 | from shbr3.a_count_positive import main 6 | from shbr3.b_boomers_and_zoomers import count_invitations 7 | from shbr3.c_festive_lights import count_max_lights, make_x_same_lights 8 | from shbr3.d_cows_in_stalls import can_fit_at_x_distance, max_min_distance 9 | from shbr3.e_brewing_potions import max_sum_quality 10 | 11 | 12 | @pytest.mark.parametrize( 13 | 'test_input, expected', 14 | ( 15 | ( 16 | [ 17 | '5', 18 | '2 -1 2 -2 3', 19 | '4', 20 | '1 1', 21 | '1 3', 22 | '2 4', 23 | '1 5', 24 | ], 25 | ['1', '2', '1', '3'], 26 | ), 27 | ), 28 | ) 29 | def test_a(test_input, expected, monkeypatch, capsys): 30 | inputs = StringIO('\n'.join(test_input)) 31 | monkeypatch.setattr('sys.stdin', inputs) 32 | main() 33 | assert capsys.readouterr().out.strip().split('\n') == expected 34 | 35 | 36 | @pytest.mark.parametrize( 37 | 'test_input, expected', 38 | ( 39 | ([16, 16], 2), 40 | ([17, 16, 18], 2), 41 | ([120, 25, 30, 100, 105], 4), 42 | ([16, 16, 16], 6), 43 | ([16, 16, 16, 16], 12), 44 | ([16, 17, 17], 4), 45 | ([16, 16, 17, 17, 17], 14), 46 | ([7, 7, 28], 0), 47 | ), 48 | ) 49 | def test_b(test_input, expected): 50 | assert count_invitations(test_input) == expected 51 | 52 | 53 | @pytest.mark.parametrize( 54 | 'sorted_counts, lights_len, x, expected', 55 | ( 56 | ((50, 30, 10, 6, 2), 5, 2, (5,)), 57 | ((50, 30, 10, 6, 2), 5, 10, (5,)), 58 | ((50, 30, 10, 6, 2), 5, 12, (4, 1)), 59 | ((50, 30, 10, 6, 2), 5, 15, (3, 2)), 60 | ((50, 30, 10, 6, 2), 5, 16, None), 61 | ), 62 | ) 63 | def test_make_x_same_lights(sorted_counts, lights_len, x, expected): 64 | assert make_x_same_lights(sorted_counts, lights_len, x) == expected 65 | 66 | 67 | @pytest.mark.parametrize( 68 | 'n, a, max_count, max_lights', 69 | ( 70 | (3, (3, 3, 2, 1), 2, (1, 2, 3)), 71 | (6, (15, 10, 8, 7, 6, 5, 1), 6, (1, 1, 2, 3, 4, 5)), 72 | (5, (50, 30, 10, 6, 2), 15, (1, 1, 1, 2, 2)), 73 | ), 74 | ) 75 | def test_c(n, a, max_count, max_lights): 76 | assert count_max_lights(a, n) == (max_count, max_lights) 77 | 78 | 79 | @pytest.mark.parametrize( 80 | 'a, total_count, x, expected', 81 | ( 82 | ([2, 5, 7, 11, 15, 20], 6, 2, True), 83 | ([2, 5, 7, 11, 15, 20], 6, 3, False), 84 | ([2, 5, 7, 11, 15, 20], 3, 9, True), 85 | ([2, 5, 7, 11, 15, 20], 3, 10, False), 86 | ), 87 | ) 88 | def test_can_fit_at_x_distance(a, total_count, x, expected): 89 | assert can_fit_at_x_distance(a, total_count, x) == expected 90 | 91 | 92 | @pytest.mark.parametrize( 93 | 'a, k, expected', 94 | ( 95 | ([2, 5, 7, 11, 15, 20], 2, 18), 96 | ([2, 5, 7, 11, 15, 20], 3, 9), 97 | ([2, 5, 7, 11, 15, 20], 4, 5), 98 | ([2, 5, 7, 11, 15, 20], 5, 4), 99 | ([2, 5, 7, 11, 15, 20], 6, 2), 100 | ), 101 | ) 102 | def test_d(a, k, expected): 103 | assert max_min_distance(a, k) == expected 104 | 105 | 106 | @pytest.mark.parametrize( 107 | 'a, k, expected', 108 | ( 109 | ([-2, 3, -5, 5, 1], 5, 26), 110 | ([-1, 1], 2, 1), 111 | ([101, 100, 3, 2, 1], 1, 201), 112 | ([101, 100, 3, 2, 1], 2, 305), 113 | ([101, 100, 3, 2, 1], 3, 408), 114 | ([101, 100, 3, 2, 1], 4, 511), 115 | ([101, 100, 3, 2, 1], 5, 613), 116 | ([101, 100, 3, 2, 1], 6, 715), 117 | ([101, 100, 3, 2, 1], 7, 816), 118 | ([101, 100, 3, 2, 1], 8, 917), 119 | ([101, 100, 3, 2, 1], 9, 1017), 120 | ([101, 100, 3, 2, 1], 10, 1022), 121 | ([101, 100, 3, 2, 1], 11, 1026), 122 | ([101, 100, 3, 2, 1], 12, 1029), 123 | ([101, 100, 3, 2, 1], 13, 1032), 124 | ([101, 100, 3, 2, 1], 14, 1034), 125 | ([101, 100, 3, 2, 1], 15, 1035), 126 | ), 127 | ) 128 | def test_e(a, k, expected): 129 | assert max_sum_quality(a, k) == expected 130 | -------------------------------------------------------------------------------- /bonus2/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bonus2 import ( 4 | a_lamps, 5 | b_balanced_tree, 6 | c_anagram_tree, 7 | d_tree_twins, 8 | e_search_tree, 9 | f_max_depth, 10 | g_max_path, 11 | h_num_paths, 12 | i_different_search_trees, 13 | j_add_node, 14 | k_show_range, 15 | l_sieve_down, 16 | m_sieve_up, 17 | n_tree_partition, 18 | ) 19 | 20 | 21 | def test_a(): 22 | node1 = a_lamps.Node(1) 23 | node2 = a_lamps.Node(-5) 24 | node3 = a_lamps.Node(3, node1, node2) 25 | node4 = a_lamps.Node(2, node3, None) 26 | assert a_lamps.solution(node4) == 3 27 | 28 | 29 | def test_b(): 30 | node1 = b_balanced_tree.Node(1) 31 | node2 = b_balanced_tree.Node(-5) 32 | node3 = b_balanced_tree.Node(3, node1, node2) 33 | node4 = b_balanced_tree.Node(10) 34 | node5 = b_balanced_tree.Node(2, node3, node4) 35 | assert b_balanced_tree.solution(node5) 36 | 37 | 38 | def test_c(): 39 | node1 = c_anagram_tree.Node(3, None, None) 40 | node2 = c_anagram_tree.Node(4, None, None) 41 | node3 = c_anagram_tree.Node(4, None, None) 42 | node4 = c_anagram_tree.Node(3, None, None) 43 | node5 = c_anagram_tree.Node(2, node1, node2) 44 | node6 = c_anagram_tree.Node(2, node3, node4) 45 | node7 = c_anagram_tree.Node(1, node5, node6) 46 | assert c_anagram_tree.solution(node7) 47 | 48 | 49 | def test_d(): 50 | node1 = d_tree_twins.Node(1, None, None) 51 | node2 = d_tree_twins.Node(2, None, None) 52 | node3 = d_tree_twins.Node(3, node1, node2) 53 | node4 = d_tree_twins.Node(1, None, None) 54 | node5 = d_tree_twins.Node(2, None, None) 55 | node6 = d_tree_twins.Node(3, node4, node5) 56 | assert d_tree_twins.solution(node3, node6) 57 | 58 | 59 | def test_e(): 60 | node1 = e_search_tree.Node(1, None, None) 61 | node2 = e_search_tree.Node(4, None, None) 62 | node3 = e_search_tree.Node(3, node1, node2) 63 | node4 = e_search_tree.Node(8, None, None) 64 | node5 = e_search_tree.Node(5, node3, node4) 65 | assert e_search_tree.solution(node5) 66 | node2.value = 5 67 | assert not e_search_tree.solution(node5) 68 | 69 | 70 | def test_f(): 71 | node1 = f_max_depth.Node(1, None, None) 72 | node2 = f_max_depth.Node(4, None, None) 73 | node3 = f_max_depth.Node(3, node1, node2) 74 | node4 = f_max_depth.Node(8, None, None) 75 | node5 = f_max_depth.Node(5, node3, node4) 76 | assert f_max_depth.solution(node5) == 3 77 | 78 | 79 | def test_g(): 80 | node1 = g_max_path.Node(5, None, None) 81 | node2 = g_max_path.Node(1, None, None) 82 | node3 = g_max_path.Node(-3, node2, node1) 83 | node4 = g_max_path.Node(2, None, None) 84 | node5 = g_max_path.Node(2, node4, node3) 85 | assert g_max_path.solution(node5) == 6 86 | 87 | 88 | def test_h(): 89 | node1 = h_num_paths.Node(2, None, None) 90 | node2 = h_num_paths.Node(1, None, None) 91 | node3 = h_num_paths.Node(3, node1, node2) 92 | node4 = h_num_paths.Node(2, None, None) 93 | node5 = h_num_paths.Node(1, node4, node3) 94 | assert h_num_paths.solution(node5) == 275 95 | 96 | 97 | @pytest.mark.parametrize( 98 | 'test_input, expected', 99 | ( 100 | (2, 2), 101 | (3, 5), 102 | (4, 14), 103 | ), 104 | ) 105 | def test_i(test_input, expected): 106 | assert i_different_search_trees.count_distinct(test_input) == expected 107 | 108 | 109 | def test_j(): 110 | node1 = j_add_node.Node(None, None, 7) 111 | node2 = j_add_node.Node(node1, None, 8) 112 | node3 = j_add_node.Node(None, node2, 7) 113 | new_head = j_add_node.insert(node3, 6) 114 | assert new_head is node3 115 | assert new_head.left.value == 6 # type: ignore 116 | 117 | 118 | def test_k(capsys): 119 | node1 = k_show_range.Node(None, None, 2) 120 | node2 = k_show_range.Node(None, node1, 1) 121 | node3 = k_show_range.Node(None, None, 8) 122 | node4 = k_show_range.Node(None, node3, 8) 123 | node5 = k_show_range.Node(node4, None, 9) 124 | node6 = k_show_range.Node(node5, None, 10) 125 | node7 = k_show_range.Node(node2, node6, 5) 126 | k_show_range.print_range(node7, 2, 8) 127 | assert capsys.readouterr().out.strip().split('\n') == ['2', '5', '8', '8'] 128 | 129 | 130 | def test_l(): 131 | sample = [-1, 12, 1, 8, 3, 4, 7] 132 | assert l_sieve_down.sift_down(sample, 2) == 5 133 | 134 | 135 | def test_m(): 136 | sample = [-1, 12, 6, 8, 3, 15, 7] 137 | assert m_sieve_up.sift_up(sample, 5) == 1 138 | 139 | 140 | def test_n(): 141 | node1 = n_tree_partition.Node(None, None, 3, 1) 142 | node2 = n_tree_partition.Node(None, node1, 2, 2) 143 | node3 = n_tree_partition.Node(None, None, 8, 1) 144 | node4 = n_tree_partition.Node(None, None, 11, 1) 145 | node5 = n_tree_partition.Node(node3, node4, 10, 3) 146 | node6 = n_tree_partition.Node(node2, node5, 5, 6) 147 | left, right = n_tree_partition.split(node6, 4) 148 | assert left 149 | assert left.size == 4 150 | assert right 151 | assert right.size == 2 152 | 153 | node10 = n_tree_partition.Node(value=10, left=None, right=None, size=1) 154 | node9 = n_tree_partition.Node(value=7, left=None, right=None, size=1) 155 | node8 = n_tree_partition.Node(value=8, left=node9, right=node10, size=3) 156 | node7 = n_tree_partition.Node(value=5, left=None, right=None, size=1) 157 | node6 = n_tree_partition.Node(value=4, left=None, right=node7, size=2) 158 | node5 = n_tree_partition.Node(value=2, left=None, right=None, size=1) 159 | node4 = n_tree_partition.Node(value=3, left=node5, right=node6, size=4) 160 | node3 = n_tree_partition.Node(value=6, left=node4, right=node8, size=8) 161 | node2 = n_tree_partition.Node(value=1, left=None, right=node3, size=9) 162 | node1 = n_tree_partition.Node(value=9, left=node2, right=None, size=10) 163 | left, right = n_tree_partition.split(node1, 7) 164 | assert left 165 | assert left.size == 7 166 | assert right 167 | assert right.size == 3 168 | -------------------------------------------------------------------------------- /bonus1/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | from io import StringIO 2 | 3 | import pytest 4 | 5 | from bonus1 import b_crash_me, c_prefix_hashes 6 | from bonus1.a_polynomial_hash import polynomial_hash 7 | from bonus1.d_workshops import unique 8 | from bonus1.e_substrings import max_substring_with_no_repeatitions 9 | from bonus1.f_anagram_grouping import anagram_groups 10 | from bonus1.g_competition import longest_balanced 11 | from bonus1.h_weird_comparison import weird_compare 12 | from bonus1.i_common_subarray import longest_common_subarray_len 13 | from bonus1.j_sum_of_four import find_sum_groups 14 | from bonus1.k_nearest_stop import subway_with_most_stops 15 | from bonus1.l_mnogogosha import substrings_k_times 16 | 17 | 18 | @pytest.mark.parametrize( 19 | 'a, m, s, expected', 20 | ( 21 | (123, 100003, 'a', 97), 22 | (123, 100003, 'hash', 6080), 23 | (123, 100003, 'HaSH', 56156), 24 | ), 25 | ) 26 | def test_a(a, m, s, expected): 27 | assert polynomial_hash(a, m, s) == expected 28 | 29 | 30 | def test_b(): 31 | s1, s2 = b_crash_me.find_collision() 32 | assert b_crash_me.polynomial_hash(s1) == b_crash_me.polynomial_hash(s2) 33 | 34 | 35 | def test_prefix_hashes(): 36 | a, m, s = 1000, 1000009, 'abcdefgh' 37 | prefixes = c_prefix_hashes.prefix_hashes(a, m, s) 38 | for i, h in enumerate(prefixes): 39 | assert h == polynomial_hash(a, m, s[: i + 1]) 40 | 41 | 42 | def test_substring_hash(): 43 | a, m, s = 1000, 1000009, 'abcdefgh' 44 | prefixes = c_prefix_hashes.prefix_hashes(a, m, s) 45 | for i in range(len(s)): 46 | for j in range(i, len(s)): 47 | test_value = c_prefix_hashes.substring_hash(a, m, prefixes, i, j) 48 | expected = polynomial_hash(a, m, s[i : j + 1]) 49 | assert test_value == expected 50 | 51 | 52 | @pytest.mark.parametrize( 53 | 'test_input, expected', 54 | ( 55 | ( 56 | [ 57 | '1000', 58 | '1000009', 59 | 'abcdefgh', 60 | '7', 61 | '1 1', 62 | '1 5', 63 | '2 3', 64 | '3 4', 65 | '4 4', 66 | '1 8', 67 | '5 8', 68 | ], 69 | [ 70 | '97', 71 | '225076', 72 | '98099', 73 | '99100', 74 | '100', 75 | '436420', 76 | '193195', 77 | ], 78 | ), 79 | ( 80 | [ 81 | '100', 82 | '10', 83 | 'a', 84 | '1', 85 | '1 1', 86 | ], 87 | [ 88 | '7', 89 | ], 90 | ), 91 | ), 92 | ) 93 | def test_c(test_input, expected, monkeypatch, capsys): 94 | inputs = StringIO('\n'.join(test_input)) 95 | monkeypatch.setattr('sys.stdin', inputs) 96 | c_prefix_hashes.main() 97 | assert capsys.readouterr().out.strip().split('\n') == expected 98 | 99 | 100 | @pytest.mark.parametrize( 101 | 'test_input, expected', 102 | ( 103 | ( 104 | [ 105 | 'вышивание крестиком', 106 | 'рисование мелками на парте', 107 | 'настольный керлинг', 108 | 'настольный керлинг', 109 | 'кухня африканского племени ужасмай', 110 | 'тяжелая атлетика', 111 | 'таракановедение', 112 | 'таракановедение', 113 | ], 114 | [ 115 | 'вышивание крестиком', 116 | 'рисование мелками на парте', 117 | 'настольный керлинг', 118 | 'кухня африканского племени ужасмай', 119 | 'тяжелая атлетика', 120 | 'таракановедение', 121 | ], 122 | ), 123 | ([1, 4, 2, 5, 4, 2, 3, 1, 2], [1, 4, 2, 5, 3]), 124 | ), 125 | ) 126 | def test_d(test_input, expected): 127 | assert unique(test_input) == expected 128 | 129 | 130 | @pytest.mark.parametrize( 131 | 'test_input, expected', 132 | ( 133 | ('abcabcbb', 3), 134 | ('bbbbb', 1), 135 | ('awe', 3), 136 | ), 137 | ) 138 | def test_e(test_input, expected): 139 | assert max_substring_with_no_repeatitions(test_input) == expected 140 | 141 | 142 | @pytest.mark.parametrize( 143 | 'test_input, expected', 144 | ( 145 | (['tan', 'eat', 'tea', 'ate', 'nat', 'bat'], [[0, 4], [1, 2, 3], [5]]), 146 | (['tan', 'nat', 'tantan'], [[0, 1], [2]]), 147 | ), 148 | ) 149 | def test_f(test_input, expected): 150 | assert anagram_groups(test_input) == expected 151 | 152 | 153 | @pytest.mark.parametrize( 154 | 'test_input, expected', 155 | ( 156 | ('0 1', 2), 157 | ('0 1 0', 2), 158 | ('0 0 1 0 1 1 1 0 0 0', 8), 159 | ('1 0 0 0 0 1 0 1', 4), 160 | ('1 1', 0), 161 | ('1 1 1 1', 0), 162 | ('0 0 1 0 0 0 1 0 0 1', 4), 163 | ('0 0 1 0 0 1 1 1 1 1 0 0 0 0 0 1', 14), 164 | ('0 0 1 0 0 1 1 1 1 1 0 0', 12), 165 | ('1 0 0 0 1 1', 6), 166 | ('0 1 0 0 0 1 1 1', 8), 167 | ('1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0', 16), 168 | ), 169 | ) 170 | def test_g(test_input, expected): 171 | assert longest_balanced(test_input.split()) == expected 172 | 173 | 174 | @pytest.mark.parametrize( 175 | 's, t, expected', 176 | ( 177 | ('mxyskaoghi', 'qodfrgmslc', True), 178 | ('agg', 'xdd', True), 179 | ('agg', 'xda', False), 180 | ('abacaba', 'xhxixhx', True), 181 | ('abc', 'aaa', False), 182 | ('agg', 'pap', False), 183 | ('aabba', 'ccddc', True), 184 | ('aabba', 'ccddd', False), 185 | ), 186 | ) 187 | def test_h(s, t, expected): 188 | assert weird_compare(s, t) == expected 189 | 190 | 191 | @pytest.mark.parametrize( 192 | 'a, b, expected', 193 | ( 194 | ([1, 2, 3, 2, 1], [3, 2, 1, 5, 6], 3), 195 | ([1, 2, 3, 4, 5], [4, 5, 9], 2), 196 | ([1], [1], 1), 197 | ([1], [2], 0), 198 | ([0, 1, 2, 1, 0, 3, 2, 1], [2, 1, 0, 3, 1, 0, 2, 1, 0, 3, 2, 0], 5), 199 | ), 200 | ) 201 | def test_i(a, b, expected): 202 | assert longest_common_subarray_len(a, b) == expected 203 | 204 | 205 | @pytest.mark.parametrize( 206 | 'nums, target, expected', 207 | ( 208 | ( 209 | [2, 3, 2, 4, 1, 10, 3, 0], 210 | 10, 211 | [ 212 | (0, 3, 3, 4), 213 | (1, 2, 3, 4), 214 | (2, 2, 3, 3), 215 | ], 216 | ), 217 | ( 218 | [1, 0, -1, 0, 2, -2], 219 | 0, 220 | [ 221 | (-2, -1, 1, 2), 222 | (-2, 0, 0, 2), 223 | (-1, 0, 0, 1), 224 | ], 225 | ), 226 | ( 227 | [1, 1, 1, 1, 1], 228 | 4, 229 | [ 230 | (1, 1, 1, 1), 231 | ], 232 | ), 233 | ( 234 | [ 235 | 875179293, 236 | -635277717, 237 | -806161990, 238 | -683047340, 239 | -764850536, 240 | -201952280, 241 | 4851510, 242 | -263638039, 243 | 500234731, 244 | 551296021, 245 | -111930528, 246 | 39938747, 247 | -746984919, 248 | 83074719, 249 | 745702422, 250 | -313837990, 251 | -730402707, 252 | -585701463, 253 | -240894220, 254 | -302622779, 255 | ], 256 | -922696729, 257 | [ 258 | (-764850536, -730402707, -302622779, 875179293), 259 | (-746984919, -263638039, 4851510, 83074719), 260 | (-635277717, -585701463, -201952280, 500234731), 261 | ], 262 | ), 263 | ( 264 | [1, 1, 1, 2, 2, 2, 10, 20, 30, 40], 265 | 53, 266 | [ 267 | (1, 2, 10, 40), 268 | (1, 2, 20, 30), 269 | ], 270 | ), 271 | ), 272 | ) 273 | def test_j(nums, target, expected): 274 | assert find_sum_groups(nums, target) == expected 275 | 276 | 277 | @pytest.mark.parametrize( 278 | 'subways, stops, expected', 279 | ( 280 | ([(-1, 0), (1, 0), (2, 5)], [(10, 0), (20, 0), (22, 5)], 3), 281 | ([(-1, 0), (1, 0), (0, 5)], [(10, 0), (20, 0), (20, 5)], 2), 282 | ), 283 | ) 284 | def test_k(subways, stops, expected): 285 | assert subway_with_most_stops(subways, stops) == expected 286 | 287 | 288 | @pytest.mark.parametrize( 289 | 's, n, k, expected', 290 | ( 291 | ('gggggooooogggggoooooogggggssshaa', 10, 2, [0, 5]), 292 | ('allallallallalla', 3, 4, [0, 1, 2]), 293 | ), 294 | ) 295 | def test_l(s, n, k, expected): 296 | assert substrings_k_times(s, n, k) == expected 297 | -------------------------------------------------------------------------------- /sprint3/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | import random 2 | from io import StringIO 3 | from itertools import cycle, islice 4 | 5 | import pytest 6 | 7 | from sprint3.a_bracket_gen import bracket_gen 8 | from sprint3.b_combinations import phone_combinations, phone_combinations_rec 9 | from sprint3.c_subsequence import is_subsequence 10 | from sprint3.d_cookies import count_happy 11 | from sprint3.e_houses import count_houses, count_houses_acc 12 | from sprint3.f_triangle import max_perimeter 13 | from sprint3.final_broken_search import broken_search 14 | from sprint3.final_efficient_quicksort import main, quicksort 15 | from sprint3.g_wardrobe import counting_sort 16 | from sprint3.h_large_number import largest_number 17 | from sprint3.i_conference_fans import top_k_schools 18 | from sprint3.j_bubble import run_bubble_sort 19 | from sprint3.k_merge_sort import merge, merge_sort 20 | from sprint3.l_two_bicycles import ( 21 | bisect, 22 | left_binary_search, 23 | left_binary_search_rec, 24 | ) 25 | from sprint3.m_golden_mean import median_two 26 | from sprint3.n_flowerbeds import union 27 | from sprint3.o_index_difference import min_diff_by_index 28 | from sprint3.p_partial_sort import count_max_blocks 29 | 30 | 31 | def test_a(): 32 | assert list(bracket_gen(3)) == [ 33 | '((()))', 34 | '(()())', 35 | '(())()', 36 | '()(())', 37 | '()()()', 38 | ] 39 | 40 | for n in range(11): 41 | brackets = list(bracket_gen(n)) 42 | assert brackets == sorted(brackets) 43 | assert len(brackets) == len(set(brackets)) 44 | 45 | 46 | @pytest.mark.parametrize('func', (phone_combinations, phone_combinations_rec)) 47 | def test_b(func): 48 | assert ' '.join(func('23')) == 'ad ae af bd be bf cd ce cf' 49 | assert ' '.join(func('92')) == 'wa wb wc xa xb xc ya yb yc za zb zc' 50 | 51 | 52 | def test_c(): 53 | assert is_subsequence('abc', 'ahbgdcu') 54 | assert not is_subsequence('abcp', 'ahpc') 55 | assert is_subsequence('abc', 'xxxxxaxxxxbxxxxcxxx') 56 | assert not is_subsequence('abc', 'aaaabbb') 57 | assert not is_subsequence('abcd', 'abcp') 58 | 59 | 60 | def test_d(): 61 | assert count_happy([1, 2], [2, 1, 3]) == 2 62 | assert count_happy([2, 1, 3], [1, 1]) == 1 63 | assert count_happy([1, 1, 1, 5, 7], [1, 1, 2, 3, 3, 3, 6]) == 4 64 | 65 | 66 | @pytest.mark.parametrize('func', (count_houses, count_houses_acc)) 67 | @pytest.mark.parametrize( 68 | 'houses, budget, expected', 69 | ( 70 | ([999, 999, 999], 300, 0), 71 | ([350, 999, 200], 1000, 2), 72 | ([350, 999, 200], 2000, 3), 73 | ), 74 | ) 75 | def test_e(func, houses, budget, expected): 76 | assert func(houses, budget) == expected 77 | 78 | 79 | def test_f(): 80 | assert max_perimeter([6, 3, 3, 2]) == 8 81 | assert max_perimeter([5, 3, 7, 2, 8, 3]) == 20 82 | 83 | 84 | def test_g(): 85 | assert list(counting_sort([0, 2, 1, 2, 0, 0, 1])) == [0, 0, 0, 1, 1, 2, 2] 86 | 87 | 88 | @pytest.mark.parametrize( 89 | 'test_input, expected', 90 | ( 91 | ( 92 | ['15', '56', '2'], 93 | '56215', 94 | ), 95 | ( 96 | ['1', '783', '2'], 97 | '78321', 98 | ), 99 | ( 100 | ['2', '4', '5', '2', '10'], 101 | '542210', 102 | ), 103 | ( 104 | ['9', '10', '1', '1', '1', '6'], 105 | '9611110', 106 | ), 107 | ( 108 | ['8', '89', '87'], 109 | '89887', 110 | ), 111 | ), 112 | ) 113 | def test_h(test_input, expected): 114 | assert largest_number(test_input) == expected 115 | 116 | 117 | def test_i(): 118 | assert top_k_schools([1, 2, 3, 1, 2, 3, 4], 3) == [1, 2, 3] 119 | assert top_k_schools([3, 2, 1, 3, 1, 2, 5, 4], 4) == [1, 2, 3, 4] 120 | assert top_k_schools([2, 1], 1) == [1] 121 | 122 | 123 | @pytest.mark.parametrize( 124 | 'test_input, expected', 125 | ( 126 | ( 127 | [4, 3, 9, 2, 1], 128 | [ 129 | '3 4 2 1 9', 130 | '3 2 1 4 9', 131 | '2 1 3 4 9', 132 | '1 2 3 4 9', 133 | ], 134 | ), 135 | ( 136 | [1, 2, 3, 4, 5], 137 | [ 138 | '1 2 3 4 5', 139 | ], 140 | ), 141 | ), 142 | ) 143 | def test_j(test_input, expected, capsys): 144 | run_bubble_sort(test_input) 145 | assert capsys.readouterr().out.strip().split('\n') == expected 146 | 147 | 148 | def test_k(): 149 | a = [] 150 | merge_sort(a, 0, len(a)) 151 | assert a == [] 152 | a = [1] 153 | merge_sort(a, 0, len(a)) 154 | assert a == [1] 155 | a = [1, 1, 1] 156 | merge_sort(a, 0, len(a)) 157 | assert a == [1, 1, 1] 158 | a = [0, 1, 2, 3, 4, 5] 159 | merge_sort(a, 0, len(a)) 160 | assert a == [0, 1, 2, 3, 4, 5] 161 | a = [5, 2, 1, 0, 3, 4] 162 | merge_sort(a, 0, len(a)) 163 | assert a == [0, 1, 2, 3, 4, 5] 164 | a = [5, 2, 3, 1, 0, 3, 4, 0] 165 | merge_sort(a, 0, len(a)) 166 | assert a == [0, 0, 1, 2, 3, 3, 4, 5] 167 | 168 | a = [1, 4, 9, 2, 10, 11] 169 | assert merge(a, 0, 3, 6) == [1, 2, 4, 9, 10, 11] 170 | a = [1, 4, 2, 10, 1, 2] 171 | merge_sort(a, 0, 6) 172 | assert a == [1, 1, 2, 2, 4, 10] 173 | 174 | 175 | @pytest.mark.parametrize( 176 | 'func', (bisect, left_binary_search, left_binary_search_rec) 177 | ) 178 | @pytest.mark.parametrize( 179 | 'a, k, expected', 180 | ( 181 | ([], 1, -1), 182 | ([0], 1, -1), 183 | ([1], 1, 1), 184 | ([1, 2, 4, 4, 6, 8], 3, 3), 185 | ([1, 2, 4, 4, 6, 8], 6, 5), 186 | ([0, 1, 1, 1], 1, 2), 187 | ([0, 1, 2, 2, 2, 3, 4], 2, 3), 188 | ([0, 1, 2], 3, -1), 189 | ), 190 | ) 191 | def test_l(func, a, k, expected): 192 | assert func(a, k) == expected 193 | 194 | 195 | @pytest.mark.parametrize( 196 | 'a, b, expected', 197 | ( 198 | ([1, 3], [2], 2), 199 | ([1, 3], [2, 2, 2, 2, 2], 2), 200 | ([1, 2], [3], 2), 201 | ([2], [2, 2], 2), 202 | ([2], [2], 2), 203 | ([1, 2], [3, 4], 2.5), 204 | ([1, 4], [2, 3], 2.5), 205 | ([1, 3, 5], [2, 4], 3), 206 | ([1, 2, 3, 4, 5, 6], [10], 4), 207 | ([1, 2, 3, 4, 5, 10], [6], 4), 208 | ([1, 1, 2, 5, 6, 7], [3, 4, 4, 5, 9, 9, 9, 10, 11, 12], 5.5), 209 | ([1, 1], [3, 3], 2), 210 | ), 211 | ) 212 | def test_m(a, b, expected): 213 | result = median_two(a, b) 214 | assert result == expected 215 | assert type(result) is type(expected) 216 | 217 | 218 | @pytest.mark.parametrize( 219 | 'test_input, expected', 220 | ( 221 | ( 222 | [[7, 8], [7, 8], [2, 3], [6, 10]], 223 | [[2, 3], [6, 10]], 224 | ), 225 | ( 226 | [[2, 3], [5, 6], [3, 4], [3, 4]], 227 | [[2, 4], [5, 6]], 228 | ), 229 | ( 230 | [[1, 3], [3, 5], [4, 6], [5, 6], [2, 4], [7, 10]], 231 | [[1, 6], [7, 10]], 232 | ), 233 | ( 234 | [[7, 8], [6, 7], [1, 3], [1, 4], [2, 5]], 235 | [[1, 5], [6, 8]], 236 | ), 237 | ), 238 | ) 239 | def test_n(test_input, expected): 240 | assert list(union(test_input)) == expected 241 | 242 | 243 | @pytest.mark.parametrize( 244 | 'nums, index, expected', 245 | ( 246 | ([2, 3, 4], 2, 1), 247 | ([1, 3, 1], 1, 0), 248 | ([1, 3, 5], 3, 4), 249 | ([7, 2, 7, 3], 4, 4), 250 | ([9, 1, 10, 3, 4, 6, 2, 7], 6, 2), 251 | ), 252 | ) 253 | def test_o(nums, index, expected): 254 | assert min_diff_by_index(nums, index) == expected 255 | 256 | 257 | @pytest.mark.parametrize( 258 | 'test_input, expected', 259 | ( 260 | ([0, 1, 3, 2], 3), 261 | ([3, 6, 7, 4, 1, 5, 0, 2], 1), 262 | ([1, 0, 2, 3, 4], 4), 263 | ), 264 | ) 265 | def test_p(test_input, expected): 266 | assert count_max_blocks(test_input) == expected 267 | 268 | 269 | @pytest.mark.parametrize( 270 | 'test_input, target, expected', 271 | ( 272 | ([19, 21, 100, 101, 1, 4, 5, 7, 12], 5, 6), 273 | ([5, 1], 1, 1), 274 | ([], 1, -1), 275 | ([0], 1, -1), 276 | ([1], 1, 0), 277 | ), 278 | ) 279 | def test_final_search_in_broken_list(test_input, target, expected): 280 | assert broken_search(test_input, target) == expected 281 | 282 | 283 | def test_final_search_in_broken_list_random(): 284 | n = 10 285 | lists = [list(islice(cycle(range(1, 1 + n)), i, i + n)) for i in range(n)] 286 | for a in lists: 287 | for i, value in enumerate(a): 288 | assert broken_search(a, value) == i 289 | 290 | 291 | @pytest.mark.parametrize( 292 | 'test_input', 293 | ( 294 | [], 295 | [1], 296 | [1, 1], 297 | [1, 2], 298 | [2, 1], 299 | [5, 1, 3, 4, 2], 300 | [1, 3, 1, 4, 0], 301 | [5, 1, 1, 2, 4, 2, 6, 0, 0], 302 | ), 303 | ) 304 | def test_quicksort(test_input): 305 | expected = sorted(test_input) 306 | quicksort(test_input) 307 | assert test_input == expected 308 | 309 | 310 | def test_quicksort_random(): 311 | random.seed(1) 312 | for _ in range(1000): 313 | a = random.choices(range(10), k=50) 314 | expected = sorted(a) 315 | quicksort(a) 316 | assert a == expected 317 | 318 | 319 | @pytest.mark.parametrize( 320 | 'test_input, expected', 321 | ( 322 | ( 323 | [ 324 | '5', 325 | 'alla 4 100', 326 | 'gena 6 1000', 327 | 'gosha 2 90', 328 | 'rita 2 90', 329 | 'timofey 4 80', 330 | ], 331 | [ 332 | 'gena', 333 | 'timofey', 334 | 'alla', 335 | 'gosha', 336 | 'rita', 337 | ], 338 | ), 339 | ( 340 | [ 341 | '5', 342 | 'alla 0 0', 343 | 'gena 0 0', 344 | 'gosha 0 0', 345 | 'rita 0 0', 346 | 'timofey 0 0', 347 | ], 348 | [ 349 | 'alla', 350 | 'gena', 351 | 'gosha', 352 | 'rita', 353 | 'timofey', 354 | ], 355 | ), 356 | ), 357 | ) 358 | def test_final_efficient_quicksort(test_input, expected, monkeypatch, capsys): 359 | inputs = StringIO('\n'.join(test_input)) 360 | monkeypatch.setattr('sys.stdin', inputs) 361 | main() 362 | assert capsys.readouterr().out.strip().split('\n') == expected 363 | -------------------------------------------------------------------------------- /sprint2/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | from io import StringIO 2 | 3 | import pytest 4 | 5 | from sprint2 import ( 6 | b_todo, 7 | c_tedious_work, 8 | d_caring_mother, 9 | e_reversed, 10 | f_stack_max, 11 | final_deque, 12 | i_limited_queue, 13 | j_list_queue, 14 | ) 15 | from sprint2.a_monitoring import transpose 16 | from sprint2.final_calc import calc 17 | from sprint2.h_bracket_sequence import is_correct_bracket_seq 18 | from sprint2.k_rec_fibonacci import fib 19 | from sprint2.l_mod_fibonacci import fib_mod 20 | 21 | 22 | def test_a(): 23 | assert transpose( 24 | [ 25 | [1, 2, 3], 26 | [0, 2, 6], 27 | [7, 4, 1], 28 | [2, 7, 0], 29 | ], 30 | 4, 31 | 3, 32 | ) == [ 33 | [1, 0, 7, 2], 34 | [2, 2, 4, 7], 35 | [3, 6, 1, 0], 36 | ] 37 | 38 | 39 | def test_b(capsys): 40 | node3 = b_todo.Node('node3', None) 41 | node2 = b_todo.Node('node2', node3) 42 | node1 = b_todo.Node('node1', node2) 43 | node0 = b_todo.Node('node0', node1) 44 | b_todo.print_linked_list(node0) 45 | captured = capsys.readouterr() 46 | stdout = captured.out 47 | assert stdout == 'node0\nnode1\nnode2\nnode3\n' 48 | 49 | 50 | def test_c(): 51 | node3 = c_tedious_work.Node('node3', None) 52 | node2 = c_tedious_work.Node('node2', node3) 53 | node1 = c_tedious_work.Node('node1', node2) 54 | node0 = c_tedious_work.Node('node0', node1) 55 | new_head = c_tedious_work.remove(node0, 1) 56 | assert new_head is node0 57 | assert new_head.next_item is node2 58 | assert new_head.next_item.next_item is node3 59 | assert new_head.next_item.next_item.next_item is None 60 | 61 | 62 | @pytest.mark.parametrize( 63 | 'func', (d_caring_mother.find, d_caring_mother.find_iter) 64 | ) 65 | def test_d(func): 66 | node3 = d_caring_mother.Node('node3', None) 67 | node2 = d_caring_mother.Node('node2', node3) 68 | node1 = d_caring_mother.Node('node1', node2) 69 | node0 = d_caring_mother.Node('node0', node1) 70 | assert func(node0, 'node0') == 0 71 | assert func(node0, 'node1') == 1 72 | assert func(node0, 'node2') == 2 73 | assert func(node0, 'node3') == 3 74 | assert func(node0, 'node4') == -1 75 | 76 | 77 | def test_e(): 78 | node3 = e_reversed.DoubleConnectedNode('node3') 79 | node2 = e_reversed.DoubleConnectedNode('node2') 80 | node1 = e_reversed.DoubleConnectedNode('node1') 81 | node0 = e_reversed.DoubleConnectedNode('node0') 82 | 83 | node0.next = node1 84 | 85 | node1.prev = node0 86 | node1.next = node2 87 | 88 | node2.prev = node1 89 | node2.next = node3 90 | 91 | node3.prev = node2 92 | 93 | new_head = e_reversed.reverse(node0) 94 | 95 | assert new_head is node3 96 | assert node3.next is node2 97 | assert node2.next is node1 98 | assert node2.prev is node3 99 | assert node1.next is node0 100 | assert node1.prev is node2 101 | assert node0.prev is node1 102 | 103 | 104 | @pytest.mark.parametrize( 105 | 'commands, expected', 106 | ( 107 | ( 108 | ( 109 | 'get_max', 110 | 'push 7', 111 | 'pop', 112 | 'push -2', 113 | 'push -1', 114 | 'pop', 115 | 'get_max', 116 | 'get_max', 117 | ), 118 | [ 119 | 'None', 120 | '-2', 121 | '-2', 122 | ], 123 | ), 124 | ( 125 | ( 126 | 'pop', 127 | 'push 1', 128 | 'push 1', 129 | 'push 1', 130 | 'get_max', 131 | 'pop', 132 | 'get_max', 133 | 'get_max', 134 | 'pop', 135 | 'pop', 136 | 'get_max', 137 | 'pop', 138 | ), 139 | [ 140 | 'error', 141 | '1', 142 | '1', 143 | '1', 144 | 'None', 145 | 'error', 146 | ], 147 | ), 148 | ( 149 | ( 150 | 'pop', 151 | 'pop', 152 | 'push 4', 153 | 'push -5', 154 | 'push 7', 155 | 'pop', 156 | 'pop', 157 | 'get_max', 158 | ), 159 | [ 160 | 'error', 161 | 'error', 162 | '4', 163 | ], 164 | ), 165 | ( 166 | ( 167 | 'get_max', 168 | 'push -6', 169 | 'pop', 170 | 'pop', 171 | 'get_max', 172 | 'push 2', 173 | 'get_max', 174 | 'pop', 175 | 'push -2', 176 | ), 177 | [ 178 | 'None', 179 | 'error', 180 | 'None', 181 | '2', 182 | ], 183 | ), 184 | ), 185 | ) 186 | def test_f(commands, expected, capsys): 187 | stack = f_stack_max.StackMax() 188 | for command in commands: 189 | f_stack_max.execute(command, stack) 190 | assert capsys.readouterr().out.split() == expected 191 | 192 | 193 | @pytest.mark.parametrize( 194 | 'test_input, expected', 195 | ( 196 | ('()', True), 197 | ('(())', True), 198 | ('{[()]}', True), 199 | ('()()()', True), 200 | ('()[]{}', True), 201 | ('([]){[]}((({})))', True), 202 | ('(', False), 203 | (')', False), 204 | ('()[', False), 205 | ('[{}(]', False), 206 | (']()]', False), 207 | ), 208 | ) 209 | def test_h(test_input, expected): 210 | assert is_correct_bracket_seq(test_input) == expected 211 | 212 | 213 | @pytest.mark.parametrize( 214 | 'max_size, commands, expected', 215 | ( 216 | ( 217 | 2, 218 | ( 219 | 'peek', 220 | 'push 5', 221 | 'push 2', 222 | 'peek', 223 | 'size', 224 | 'size', 225 | 'push 1', 226 | 'size', 227 | ), 228 | [ 229 | 'None', 230 | '5', 231 | '2', 232 | '2', 233 | 'error', 234 | '2', 235 | ], 236 | ), 237 | ( 238 | 1, 239 | ( 240 | 'push 3', 241 | 'size', 242 | 'push 4', 243 | 'size', 244 | 'push 5', 245 | 'pop', 246 | 'push 6', 247 | 'pop', 248 | ), 249 | [ 250 | '1', 251 | 'error', 252 | '1', 253 | 'error', 254 | '3', 255 | '6', 256 | ], 257 | ), 258 | ), 259 | ) 260 | def test_i(max_size, commands, expected, capsys): 261 | queue = i_limited_queue.MyQueueSized(max_size) 262 | for command in commands: 263 | i_limited_queue.execute(command, queue) 264 | assert capsys.readouterr().out.split() == expected 265 | 266 | 267 | @pytest.mark.parametrize( 268 | 'commands, expected', 269 | ( 270 | ( 271 | ( 272 | 'put -34', 273 | 'put -23', 274 | 'get', 275 | 'size', 276 | 'get', 277 | 'size', 278 | 'get', 279 | 'get', 280 | 'put 80', 281 | 'size', 282 | ), 283 | [ 284 | '-34', 285 | '1', 286 | '-23', 287 | '0', 288 | 'error', 289 | 'error', 290 | '1', 291 | ], 292 | ), 293 | ), 294 | ) 295 | def test_j(commands, expected, capsys): 296 | queue = j_list_queue.ListQueue() 297 | for command in commands: 298 | j_list_queue.execute(command, queue) 299 | assert capsys.readouterr().out.split() == expected 300 | 301 | 302 | @pytest.mark.parametrize( 303 | 'test_input, expected', 304 | ( 305 | (0, 1), 306 | (1, 1), 307 | (2, 2), 308 | (3, 3), 309 | (10, 89), 310 | ), 311 | ) 312 | def test_k(test_input, expected): 313 | assert fib(test_input) == expected 314 | 315 | 316 | @pytest.mark.parametrize( 317 | 'test_input, expected', 318 | ( 319 | ((0, 1), 1), 320 | ((1, 1), 1), 321 | ((2, 1), 2), 322 | ((3, 1), 3), 323 | ((10, 2), 89), 324 | ((10, 4), 89), 325 | ((100, 5), 84101), 326 | ((10**6, 8), 26937501), 327 | ), 328 | ) 329 | def test_l(test_input, expected): 330 | assert fib_mod(*test_input) == expected 331 | 332 | 333 | @pytest.mark.parametrize( 334 | 'test_input, expected', 335 | ( 336 | ( 337 | ( 338 | '4', 339 | '4', 340 | 'push_front 861', 341 | 'push_front -819', 342 | 'pop_back', 343 | 'pop_back', 344 | ), 345 | [ 346 | '861', 347 | '-819', 348 | ], 349 | ), 350 | ( 351 | ( 352 | '15', 353 | '4', 354 | 'pop_front', 355 | 'pop_back', 356 | 'push_back 2', 357 | 'push_front 1', 358 | 'push_back 3', 359 | 'push_front 0', 360 | 'pop_back', 361 | 'pop_front', 362 | 'push_back 4', 363 | 'push_front -1', 364 | 'pop_front', 365 | 'pop_front', 366 | 'pop_front', 367 | 'pop_front', 368 | 'pop_front', 369 | ), 370 | [ 371 | 'error', 372 | 'error', 373 | '3', 374 | '0', 375 | '-1', 376 | '1', 377 | '2', 378 | '4', 379 | 'error', 380 | ], 381 | ), 382 | ), 383 | ) 384 | def test_final_deque(test_input, expected, monkeypatch, capsys): 385 | inputs = StringIO('\n'.join(test_input)) 386 | monkeypatch.setattr('sys.stdin', inputs) 387 | final_deque.main() 388 | assert capsys.readouterr().out.split() == expected 389 | 390 | 391 | @pytest.mark.parametrize( 392 | 'test_input, expected', 393 | ( 394 | ('2 1 + 3 *', 9), 395 | ('2 5 * 3 - 2 /', 3), 396 | ), 397 | ) 398 | def test_final_calc(test_input, expected): 399 | assert calc(test_input) == expected 400 | --------------------------------------------------------------------------------