Подтема 7 из 7 · дополнительный урок
Выражения с условиями и баланс
Выражение со значением 0, не более K чисел, порядок действий, сумма после буквы и «букв вдвое больше цифр».
Значение равно нулю
Это дополнительный урок: такие условия есть в сборниках и пробниках, а задачи основных подтем решаются без него. Нужно самое длинное корректное выражение с + и *, значение которого равно нулю. Неужели придётся вычислять все выражения?
Нет. Сумма неотрицательных чисел равна нулю, только если каждое слагаемое — ноль. А произведение равно нулю, когда среди множителей есть 0. Значит, выражение — сумма произведений, и в каждом произведении есть число 0. Это снова шаблон: слагаемое — множители через *, среди которых 0.
import re s = '12*0+5*0*7+3'number = r'(?:0|[1-9][0-9]*)'term = r'(?:' + number + r'\*)*0(?:\*' + number + r')*'pattern = term + r'(?:\+' + term + r')*'for m in re.finditer(pattern, s): print(m.group())12*0 и 5*0*7 равны нулю, а слагаемое 3 — нет, на нём выражение обрывается. Внутри шаблона * пишут как \*: без обратной косой черты звёздочка значит «сколько угодно раз».
Не более K чисел
Нужно самое длинное корректное выражение, в котором не больше двух чисел. Почему нельзя просто поменять * в шаблоне на «не больше одного повтора»?
Потому что finditer отдаст непересекающиеся куски и может разрезать выражение неудачно. Надёжнее найти длинные выражения целиком, а внутри каждого перебрать окна из K соседних чисел. В строке 1+2+345678 при поиске сразу по два числа найдётся 1+2, а за ним 345678 — и лучший кусок 2+345678 потеряется.
import re s = '1+2+345678'K = 2best = 0for e in re.finditer(r'[1-9]+(?:[+*][1-9]+)*', s): numbers = list(re.finditer(r'[1-9]+', e.group())) for j in range(len(numbers)): i = max(0, j - K + 1) best = max(best, numbers[j].end() - numbers[i].start()) print(best)numbers[j].start() и numbers[j].end() — где число начинается и где кончается внутри выражения. Окно из чисел с i по j — это кусок от начала i-го числа до конца j-го. Обрезать крайние числа невыгодно: длина уменьшится, а чисел меньше не станет.
Порядок действий
Нужно выражение, в котором действия слева направо дают тот же порядок, что и правила математики. Когда это так?
Когда после первого + или − умножения уже нет. 2*3+4 считается слева направо правильно, а в 2+3*4 умножение надо сделать раньше сложения. Идём по найденному выражению и помним последний + или −. Встретили * после него — начало куска переносим за этот знак.
import re s = '6*2-3*4-5'number = r'(?:0|[1-9][0-9]*)'pattern = number + r'(?:[-+*]' + number + r')*'best = 0for match in re.finditer(pattern, s): text = match.group() start = 0 last = -1 for i in range(len(text)): if text[i] in '+-': last = i elif text[i] == '*' and last >= start: start = last + 1 best = max(best, i - start + 1) print(best)Подходят 6*2-3 и 3*4-5, по 5 символов. Всё выражение — нет: умножение 3*4 стоит после вычитания.
Сумма после буквы
Нужно наибольшее значение суммы натуральных чисел со знаками + и −, которая стоит сразу после буквы C. Самая длинная сумма — самая большая?
Нет: вычитание её уменьшает. Поэтому перебирают начальные части суммы, которые кончаются целым числом, и считают значение каждой. Обрывать число посередине невыгодно: после + целое число больше своего начала, а перед − выгоднее остановиться вовсе.
import re s = 'C12+5-30+7D3+4'best = Nonefor match in re.finditer(r'C([1-9]+(?:[-+][1-9]+)*)', s): total = 0 for term in re.finditer(r'([-+]?)([1-9]+)', match.group(1)): value = int(term.group(2)) if term.group(1) == '-': total -= value else: total += value if best is None or total > best: best = total print(best)Круглые скобки в шаблоне запоминают части совпадения: match.group(1) — сама сумма без буквы C, term.group(1) — знак, term.group(2) — число. Начальные части 12, 12+5, 12+5-30, 12+5-30+7 дают 12, 17, −13 и −6; наибольшее — 17. Сумма 3+4 стоит после D и не считается.
Баланс
Нужен самый длинный фрагмент, в котором букв ровно вдвое больше, чем цифр. Окно здесь не работает: условие то выполняется, то нет, и непонятно, когда сдвигать левую границу. Что делать?
Считать баланс: за букву +1, за цифру −2. Во фрагменте букв вдвое больше цифр, когда его баланс равен нулю — то есть когда на его границах накопленный баланс одинаковый. Для каждого значения баланса запоминаем, где оно встретилось впервые: от первого появления до текущего символа — самый длинный подходящий фрагмент с таким концом.
Запусти по шагам строку MKL1N и следи за словарём first. first = {0: -1} значит: до начала строки, на условной позиции −1, баланс был нулевым.
s = 'MKL1N'balance = 0first = {0: -1}best = 0for i in range(len(s)): balance += -2 if s[i].isdigit() else 1 if balance in first: best = max(best, i - first[balance]) else: first[balance] = i print(best)После M баланс 1, на позиции 3 — снова 1: между ними фрагмент KL1, две буквы и одна цифра. Ответ — 3. Если в строке есть символы, которые не буквы и не цифры, сначала реши по условию, дают ли они 0 или обрывают фрагмент.
Итого
- Значение 0каждое слагаемое — произведение с нулём
- Не более K чиселсначала целые выражения, потом окна из K чисел
- Порядок действийпосле + или − умножения нет
- Сумма после буквыперебор начальных частей до целого числа
- Балансодинаковый на границах — первых появлений
Проверьте себя
Ответь сначала сам, потом открой ответ.
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Выражение со значением 0
Текстовый файл 24-6.txt состоит из десятичных цифр и знаков «+» и «*». Определите максимальное количество символов в непрерывной последовательности — корректном арифметическом выражении с целыми неотрицательными числами без ведущих нулей, значение которого равно нулю. Никакие два знака не стоят рядом, порядок действий — по правилам математики.
Введите число.
Подсказка
Сумма равна нулю, когда каждое слагаемое — произведение, в котором есть число 0.
Решение
50.
Решение программой
import re s = open('24-6.txt').readline().strip()number = r'(?:0|[1-9][0-9]*)'term = r'(?:' + number + r'\*)*0(?:\*' + number + r')*'pattern = term + r'(?:\+' + term + r')*'print(max(len(m.group()) for m in re.finditer(pattern, s)))Ответ: 50
Не более двух чисел
В 9*8+76+5+4321 найди самое длинное корректное выражение, содержащее не более двух чисел.
Введите число.
Подсказка
Окно по числам выражения: от числа j − k + 1 до числа j.
Решение
6: 5+4321.
Решение программой
import re s = '9*8+76+5+4321'k = 2best = 0for e in re.finditer(r'[0-9]+(?:[-+*][0-9]+)*', s): numbers = list(re.finditer(r'[0-9]+', e.group())) for j in range(len(numbers)): i = max(0, j - k + 1) length = numbers[j].end() - numbers[i].start() best = max(best, length)print(best)Ответ: 6
Не более трёх чисел
В 1+22*333+4444 найди самое длинное корректное выражение, содержащее не более трёх чисел.
Введите число.
Подсказка
Окно по числам выражения: от числа j − k + 1 до числа j.
Решение
11: 22*333+4444.
Решение программой
import re s = '1+22*333+4444'k = 3best = 0for e in re.finditer(r'[0-9]+(?:[-+*][0-9]+)*', s): numbers = list(re.finditer(r'[0-9]+', e.group())) for j in range(len(numbers)): i = max(0, j - k + 1) length = numbers[j].end() - numbers[i].start() best = max(best, length)print(best)Ответ: 11
Не более 20 чисел
Текстовый файл 24-8.txt состоит из цифр от 1 до 9, знаков «+», «−», «*» и заглавных букв A, B, C, D. Определите максимальное количество символов в непрерывной последовательности, которая является корректным арифметическим выражением из не более чем 20 натуральных чисел и знаков операций между ними.
Введите число.
Подсказка
Выражения ищет re.finditer, а внутри каждого — окно из 20 чисел.
Решение
72.
Решение программой
import re s = open('24-8.txt').readline().strip() k = 20best = 0for e in re.finditer(r'[1-9]+(?:[-+*][1-9]+)*', s): numbers = list(re.finditer(r'[1-9]+', e.group())) for j in range(len(numbers)): i = max(0, j - k + 1) length = numbers[j].end() - numbers[i].start() best = max(best, length)print(best)Ответ: 72
Порядок слева направо
В строке 4-3*2*5-1 найди самое длинное корректное выражение, в котором порядок действий слева направо совпадает с математическим.
Введите число.
Подсказка
Умножение после вычитания переносит начало выражения на число после минуса.
Решение
7: 3*2*5-1; умножение 3*2 после вычитания 4-3 переносит начало на число 3.
Решение программой
s = '4-3*2*5-1'start = best = 0for i in range(len(s)): if s[i] == '*': minus = s.rfind('-', start, i) if minus != -1: start = minus + 1 best = max(best, i - start + 1)print(best)Ответ: 7
Восьмеричные числа, − и *
Текстовый файл 24-7.txt состоит из десятичных цифр и знаков «−» и «*». Определите максимальное количество символов в непрерывной последовательности, которая является корректным арифметическим выражением с целыми неотрицательными восьмеричными числами без ведущих нулей, в котором порядок действий слева направо совпадает с порядком действий по правилам математики. Никакие два знака не стоят рядом.
Введите число.
Подсказка
Сначала корректные выражения из восьмеричных чисел, затем внутри — перенос начала после минуса перед умножением.
Решение
47: восьмеричное число — (?:0|[1-7][0-7]*), цифры 8 и 9 разрывают запись.
Решение программой
import re s = open('24-7.txt').readline().strip() number = r'(?:0|[1-7][0-7]*)'pattern = number + r'(?:[-*]' + number + r')*'best = 0for m in re.finditer(pattern, s): e = m.group() start = 0 for i in range(len(e)): if e[i] == '*': minus = e.rfind('-', start, i) if minus != -1: start = minus + 1 best = max(best, i - start + 1)print(best)Ответ: 47
Наибольшая сумма после A
Сумма — натуральные числа со знаками + и - между ними. Какое наибольшее значение у суммы, стоящей сразу после A в строке A12-25+3?
Введите число.
Подсказка
Проверь все начальные части, которые кончаются целым числом.
Решение
12: начальные части дают 12, −13, −10.
Решение программой
import re s = 'A12-25+3'best = Nonefor m in re.finditer(r'A([1-9][0-9]*(?:[-+][1-9][0-9]*)*)', s): total = 0 for t in re.findall(r'[-+]?[0-9]+', m.group(1)): total += int(t) if best is None or total > best: best = totalprint(best)Ответ: 12
Самая длинная сумма после B
В строке B12-3CB21+5 найди самую длинную сумму (знаки + и -), стоящую сразу после буквы B; если таких несколько, выбери ту, у которой значение больше. Чему равно её значение?
Введите число.
Подсказка
Сравнивай пары (длина, значение).
Решение
26: суммы 12-3 и 21+5 одной длины 4, значения 9 и 26.
Решение программой
import re s = 'B12-3CB21+5'best = Nonefor m in re.finditer(r'B([1-9][0-9]*(?:[-+][1-9][0-9]*)*)', s): text = m.group(1) terms = re.findall(r'[-+]?[0-9]+', text) value = sum(int(t) for t in terms) if best is None or (len(text), value) > best: best = (len(text), value)print(best[1])Ответ: 26
Наибольшая сумма после C
Для файла 24-8.txt рассмотрим суммы — записи из одного или нескольких натуральных чисел, разделённых знаком «+» (других знаков в сумме нет). Найдите наибольшее значение суммы, которая стоит сразу после буквы C.
Введите число.
Подсказка
Со знаком + значение растёт с каждым числом: бери сумму целиком.
Решение
16467: со знаком «+» значение растёт с каждым числом, поэтому после каждой буквы C достаточно взять сумму целиком и выбрать наибольшую.
Решение программой
import re s = open('24-8.txt').readline().strip() best = 0for m in re.finditer(r'C([1-9]+(?:\+[1-9]+)*)', s): total = sum(int(t) for t in m.group(1).split('+')) best = max(best, total)print(best)Ответ: 16467
Самая длинная сумма после B в файле
Для файла 24-8.txt рассмотрим суммы из натуральных чисел со знаками «+» и «−» между ними. Найдите самую длинную сумму, стоящую сразу после буквы B; если таких несколько, выберите сумму с большим значением. В ответе запишите её значение.
Введите число.
Подсказка
Сравнивай пары (длина, значение); значение может быть отрицательным.
Решение
−5709.
Решение программой
import re s = open('24-8.txt').readline().strip() best = Nonefor m in re.finditer(r'B([1-9]+(?:[-+][1-9]+)*)', s): text = m.group(1) terms = re.findall(r'[-+]?[1-9]+', text) value = sum(int(t) for t in terms) if best is None or (len(text), value) > best: best = (len(text), value)print(best[1])Ответ: -5709
Поровну A и B
В строке ABBBAAB найди самый длинный фрагмент, в котором букв A и B поровну.
Введите число.
Подсказка
Баланс: +1 за A, −1 за B; запоминай первое появление каждого баланса.
Решение
6: ABBBAA — по три буквы; баланс в начале и после позиции 5 одинаков.
Решение программой
s = 'ABBBAAB'first = {0: -1}balance = best = 0for i in range(len(s)): balance += 1 if s[i] == 'A' else -1 if balance in first: best = max(best, i - first[balance]) else: first[balance] = iprint(best)Ответ: 6
Букв вдвое больше цифр
Текстовый файл 24-5.txt состоит из заглавных латинских букв и десятичных цифр. Определите максимальное количество идущих подряд символов, среди которых букв ровно в два раза больше, чем цифр.
Введите число.
Подсказка
Баланс: +1 за букву, −2 за цифру; запоминай первое появление каждого баланса.
Решение
51339.
Решение программой
s = open('24-5.txt').readline().strip()balance = 0first = {0: -1}best = 0for i in range(len(s)): if s[i].isdigit(): balance -= 2 else: balance += 1 if balance in first: best = max(best, i - first[balance]) else: first[balance] = i print(best)Ответ: 51339