-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patha_sequential.py
More file actions
142 lines (110 loc) · 7.19 KB
/
a_sequential.py
File metadata and controls
142 lines (110 loc) · 7.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# https://contest.yandex.ru/contest/26133/run-report/154297342/
#
# -- Принцип работы --
#
# --- Функция `unpack()` ---
#
# Функция `unpack()` осуществляет распаковку строк, содержащих специальным образом закодированные
# повторения подстрок. Функция обрабатывает в цикле все символы запакованной строки, выполняя
# определенную операцию в зависимости от класса очередного символа. Для хранения вложенных циклов
# повторений подстрок используется стек. В целях удобства вся строка целиком тоже считается однократным
# повторением своего содержимого, а в стек добавляется соответствующий элемент.
#
# * Если очередной символ строки — алфавитный, то мы добавляем этот символ в содержимое вершины стека.
# * Если очередной символ строки является цифрой, то мы запоминаем его значение как число повторений
# следующей за ним запакованной подстроки.
# * Если был считан символ `[`, то мы инициализируем новый цикл повторения подстроки и добавляем его
# в стек, сохраняя в элементе число повторений.
# * Если был считан символ `]`, то мы удаляем из стека внутренний цикл повторений и сохраняем его
# содержимое, умноженное на количество повторений, в новую вершину стека.
#
# --- Функция `get_longest_common_prefix()` ---
#
# Функция `get_longest_common_prefix()` последовательно распаковывает строки и находит их наибольший
# общий префикс, посимвольно сравнивая каждую распакованную строку с первой. По результатам сравнения
# очередной пары строк обновляется значение целочисленного указателя на конец общего префикса
# подмножества строк, обработанного на текущий момент. Если после проверки очередной строки окажется,
# что текущий наибольший общий префикс пустой, то функция прекращает дальнейшую распаковку строк и
# завершает работу.
#
# -- Доказательство корректности --
#
# В функции `unpack()` выполнение вложенных циклов реализовано таким образом, что обработка строки
# не завершится с ошибкой даже при задании некорректной последовательности скобок. Поскольку максимально
# возможное число повторений цикла не превышает девяти, то все циклы рано или поздно будут завершены.
#
# Поскольку операция нахождения наибольшего общего префикса ассоциативна, то, распаковывая и сравнивая
# строки попарно, функция `get_longest_common_prefix()` найдет наибольший общий префикс всех переданных
# в нее строк.
#
# -- Временная сложность --
#
# Если наибольший общий префикс не является пустым, то функция `get_longest_common_prefix()` распакует
# все переданные в нее строки. Поэтому временную сложность алгоритма можно оценить как `O(Σ(kᵢ|sᵢ|))`,
# где `|sᵢ|` — длина строки `i`, а `kᵢ` — коэффициент сжатия строки `i`, зависящий от количества
# повторений, которые содержатся в строке.
#
# -- Пространственная сложность --
#
# В ходе своей работы функция `get_longest_common_prefix()` последовательно распаковывает переданные
# в нее строки и сохраняет результат распаковки, начиная со второй строки, в одну и ту же переменную.
# Поэтому пространственную сложность алгоритма можно оценить как `O(max(kᵢ|sᵢ|))`.
from __future__ import annotations
import sys
from collections.abc import Iterable
def get_longest_common_prefix(strings: Iterable[str]) -> str:
strings_iter = iter(strings)
try:
packed_first_str = next(strings_iter)
except StopIteration:
return ''
first_str = unpack(packed_first_str)
end_pos = len(first_str)
for packed_other_str in strings_iter:
other_str = unpack(packed_other_str)
end_pos = min(end_pos, len(other_str))
pos = 0
while pos < end_pos:
if first_str[pos] != other_str[pos]:
break
pos += 1
end_pos = pos
if not end_pos:
break
return first_str[:end_pos]
type Repetition = tuple[int, list[str]]
def unpack(s: str) -> str:
repetitions_stack: list[Repetition] = []
current_repetition: Repetition = (1, [])
repetitions_stack.append(current_repetition)
repetition_count = 0
for char in s:
if char.isalpha():
current_repetition[1].append(char)
elif char.isdigit():
repetition_count = int(char)
elif char == '[':
current_repetition = (repetition_count, [])
repetitions_stack.append(current_repetition)
repetition_count = 0
elif char == ']':
if len(repetitions_stack) < 2:
continue
repetitions_stack.pop()
previous_repetition = current_repetition
current_repetition = repetitions_stack[-1]
current_repetition[1].extend(_render_repetition(previous_repetition))
return ''.join(_render_repetition(current_repetition))
def _render_repetition(repetition: Repetition) -> Iterable[str]:
content = ''.join(repetition[1])
for i in range(repetition[0]):
yield content
def read_strings(count: int) -> Iterable[str]:
for i in range(count):
yield sys.stdin.readline().strip()
def main() -> None:
strings_count = int(input().strip())
strings = read_strings(strings_count)
print(get_longest_common_prefix(strings))
if __name__ == '__main__':
main()