24. Подсчет символов: #297632
Текстовый файл состоит из цифр $0$, $1$, $2$, $3$, $4$ и знаков арифметических операций «$-$» и «$+$» (вычитание и сложение). Определите максимальное количество символов в непрерывной последовательности, которая является корректным арифметическим выражением с целыми неотрицательными числами. В этом выражении никакие два знака арифметических операций не стоят рядом, в записи чисел отсутствуют незначащие (ведущие) нули и число $0$ не имеет знака.
В ответе укажите количество символов.
Знаки операций здесь другие — вместо умножения сложение, — но на алгоритм это не влияет никак. Важно лишь то, что оба символа являются разделителями между числами, а сами по себе выражением не являются.
Отдельно отмечу оговорку «число $0$ не имеет знака». Она означает, что запись вида $+0$ или $-0$ как отдельного числа не рассматривается: знак у нас всегда бинарная операция между двумя числами, а не унарный минус или плюс. Поэтому корректное выражение обязано начинаться и заканчиваться цифрой.
Корректное выражение — это число, за которым идёт любое количество пар «знак операции плюс число». Число — либо одиночный $0$, либо цифра из $1$–$4$, за которой идут любые цифры набора $0$, $1$, $2$, $3$, $4$. Запрет ведущих нулей означает ровно одно: с нуля может начинаться только само число $0$.
Помним про подстроку: фрагмент имеет право начаться в середине блока цифр, отбросив ведущие нули. Но у обрезанного старта есть плата — продолжить его влево знаком операции нельзя, слева стоит цифра. Присоединять что-то слева можно только когда число начинается с самого начала своего блока цифр. Это и отражает переменная chain.
Идём слева направо и для каждой позиции $j$ с цифрой считаем $L[j]$ — длину самого длинного корректного выражения, кончающегося в этой позиции. Вариантов два: либо это одно число, либо к числу слева через знак операции приклеивается уже посчитанное выражение, кончавшееся на два символа левее начала числа.
s = open('24-26-1.txt').read().strip()
n = len(s)
ops = set('-+')
L = [0] * n # L[j] — длина макс. верного выражения, кончающегося в j
best = 0
r = 0 # начало текущего блока цифр
for j in range(n):
if s[j] in ops:
r = j + 1
continue
# 1) выражение из одного числа
if s[r] != '0': # блок начинается со значащей цифры
cur = j - r + 1
chain = r
elif j == r: # само число 0
cur = 1
chain = r
else: # блок начался с 0 — стартуем с первой ненулевой
q = r
while q <= j and s[q] == '0':
q += 1
cur = j - q + 1 if q <= j else 1
chain = -1 # слева цифра, знак операции не приписать
# 2) приклеиваем слева выражение через знак операции
if chain >= 2 and s[chain-1] in ops and L[chain-2] > 0:
cur = max(cur, L[chain-2] + 1 + (j - chain + 1))
L[j] = cur
best = max(best, cur)
print(best) # 144
Проверка найденного фрагмента: длина $144$, он целиком распознаётся регулярным выражением для корректной записи. Слева от него идёт $1+-$, то есть перед стартом знак, а перед ним ещё один — влево не растянуть. Справа фрагмент кончается на $2-12$, а дальше $-++$, снова два знака подряд.