Подтема 6 из 7
Числа и выражения
Наибольшее число в строке, запись числа в системе счисления, корректное выражение регулярным выражением; решаем задачу темы.
Числа в строке
В строке вперемешку буквы, цифры и знаки. Как найти наибольшее число, записанное цифрами?
Выделить все группы цифр одной командой re.findall(r'[0-9]+', s) и сравнить их как числа — через int, а не как строки. Модуль re работает с — шаблонами текста. Шаблон [0-9]+ значит «одна или больше цифр подряд».
import re s = 'a23,45bfg24,131kirt16,1fff457.'numbers = re.findall(r'[0-9]+', s)print(numbers)print(max(int(x) for x in numbers))print(max(int(x) for x in numbers if int(x) % 2 == 0))Как строки, '45' больше '131': строки сравниваются по первому символу, и 4 больше 1. Поэтому каждую группу переводим в int. Наибольшее чётное — 24: у 457 и 131 последняя цифра нечётная.
Запись в системе счисления
Нужна запись наибольшего чётного 14-ричного числа, а ответ — её длина. Цифры такого числа — 0–9 и A, B, C, D. Как её найти?
Разбить строку на блоки 14-ричных цифр. В каждом блоке лучшее чётное число — от первой ненулевой цифры до последней чётной. Среди блоков больше то число, у которого больше цифр, а при равной длине — то, что больше по цифрам слева направо.
Основание 14 чётное, поэтому чётность числа видна по последней цифре: чётны 0, 2, 4, 6, 8, A (10) и C (12). Ведущие нули значения не меняют — их отбрасывают lstrip('0'), иначе 00B2 казалось бы длиннее B2.
import re s = 'Z0A31C4F00B21D7'best = ''for block in re.findall(r'[0-9A-D]+', s): block = block.lstrip('0') end = -1 for i in range(len(block)): if block[i] in '02468AC': end = i t = block[:end + 1] if len(t) > len(best) or len(t) == len(best) and t > best: best = t print(best, len(best))Блоки — 0A31C4 и 00B21D7: Z и F не 14-ричные цифры. Из первого получается A31C4, из второго — только B2: после 2 чётных цифр нет. Сравнение строк t > best здесь работает: цифры 0–9 идут раньше букв A–D, как и их значения.
Шаблон выражения
Как найти самое длинное корректное выражение — например, в строке 03+5**7+0*12?
Описать выражение шаблоном: число, а за ним сколько угодно раз «знак, число». re.finditer найдёт все такие куски слева направо, остаётся взять самый длинный. Вычислять выражения не нужно — проверяется только запись.
Число без ведущих нулей — это либо 0, либо ненулевая цифра и за ней любые цифры. Разберём шаблон по частям:
| Часть | Что значит |
|---|---|
| [1-9] | одна цифра от 1 до 9 |
| [0-9]* | любые цифры, в том числе ни одной |
| 0|[1-9][0-9]* | либо 0, либо число без ведущего нуля |
| (?:…) | скобки: объединяют части, ничего не запоминая |
| [+*] | один знак: + или * |
| (?:[+*]число)* | «знак, число» — ноль или больше раз |
import re s = '03+5**7+0*12'number = r'(?:0|[1-9][0-9]*)'pattern = number + r'(?:[+*]' + number + r')*'for m in re.finditer(pattern, s): print(m.group())Ноль не может начинать число, поэтому первое выражение — просто 0, а следующее начинается с 3. Два знака подряд обрывают 3+5. Самое длинное — 7+0*12, 6 символов.
Шаблон меняют по условию. Знаки − и * — это [-*]; если нуля в выражении быть не может, число — [1-9][0-9]*.
Задача темы
Решим задачу темы целиком:
«Текстовый файл 24-6.txt состоит из десятичных цифр и знаков «+» и «*» (сложения и умножения). Определите максимальное количество символов в непрерывной последовательности, являющейся корректным арифметическим выражением с целыми неотрицательными числами (без знака). В этом выражении никакие два знака арифметических операций не стоят рядом, в записи чисел отсутствуют незначащие (ведущие) нули. В ответе укажите количество символов.»
Это ровно шаблон из прошлого раздела: числа без ведущих нулей, знаки + и *. Читаем строку из файла и берём самое длинное совпадение. Файл 24-6.txt приложен к задаче темы в практике этой подтемы.
import re s = open('24-6.txt').readline().strip()number = r'(?:0|[1-9][0-9]*)'pattern = number + r'(?:[+*]' + number + r')*'print(max(len(m.group()) for m in re.finditer(pattern, s)))Ответ — 172. Программа проходит 200 000 символов файла за доли секунды.
Почему finditer не пропускает самое длинное выражение
finditer берёт совпадения без перекрытий: найдя кусок, ищет дальше с его конца. Может ли лучшее выражение начинаться внутри уже найденного куска и выходить за его конец? Нет. Найденный кусок оборвался, потому что дальше запись не продолжается: два знака подряд, знак в конце, цифра после одиночного 0 или посторонний символ. Выражение, начатое внутри куска, упрётся в то же место — значит, оно не длиннее найденного.
Итого
- Все числа
re.findall(r'[0-9]+', s) - Сравнениечерез int, не как строки
- Система счисленияблоки цифр, без ведущих нулей, длина, потом цифры
- Число без ведущих нулей
(?:0|[1-9][0-9]*) - Выражениечисло + «знак, число» сколько угодно раз
- Ответmax длины по re.finditer; задача темы — 172
Проверьте себя
Ответь сначала сам, потом открой ответ.
max(['45', '131']) даёт неверный ответ на вопрос «какое число больше»?ОтветСкрыть ответ
(?:0|[1-9][0-9]*)?ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Наибольшее чётное число в файле
Текстовый файл 24-5.txt состоит из заглавных латинских букв и десятичных цифр. Числом считается последовательность цифр, ограниченная отличными от цифр символами, началом или концом строки. Определите наибольшее чётное число в файле.
Введите число.
Подсказка
Среди всех чисел оставь те, что делятся на 2, и возьми наибольшее через int.
Решение
95225202546.
Решение программой
import re s = open('24-5.txt').readline().strip()numbers = re.findall(r'[0-9]+', s)print(max(int(x) for x in numbers if int(x) % 2 == 0))Ответ: 95225202546
Число с основанием 12
Найди самую длинную запись числа с основанием 12 (цифры 0–9, A, B) без ведущего нуля в строке 0A9BC00B1A20X.
Введите число.
Подсказка
Первая цифра — от 1 до B, остальные — любые цифры основания 12.
Решение
5: B1A20; C — не цифра основания 12, а запись 00B1A20 начинать с нуля нельзя.
Решение программой
import re s = '0A9BC00B1A20X'pattern = r'[1-9AB][0-9AB]*'print(max(len(m.group()) for m in re.finditer(pattern, s)))Ответ: 5
Число с основанием 20
Текстовый файл 24-5.txt состоит из заглавных латинских букв и десятичных цифр. Найдите самую длинную подстроку, которая может являться записью числа в системе счисления с основанием 20 (цифры 0–9 и A–J). Число не может начинаться с нуля. В ответе запишите длину подстроки.
Введите число.
Подсказка
Цифры 0–9 и A–J; первая — не 0.
Решение
22.
Решение программой
import re s = open('24-5.txt').readline().strip() pattern = r'[1-9A-J][0-9A-J]*'print(max(len(m.group()) for m in re.finditer(pattern, s)))Ответ: 22
Наибольшее чётное в основании 16
В файле 24-5.txt найдите последовательность идущих подряд символов, которая является записью наибольшего чётного числа в системе счисления с основанием 16 (цифры 0–9 и A–F). В ответе запишите количество символов в записи этого числа без незначащих нулей.
Введите число.
Подсказка
Убери ведущие нули блока, обрежь его до последней чётной цифры и сравнивай по длине, затем по записи.
Решение
18.
Решение программой
import re s = open('24-5.txt').readline().strip() digits = '0123456789ABCDEF'best = ''for m in re.finditer('[0-9A-F]+', s): block = m.group().lstrip('0') end = -1 for i in range(len(block)): if digits.index(block[i]) % 2 == 0: end = i if end >= 0: candidate = block[:end + 1] else: candidate = '0' if '0' in m.group() else '' if (len(candidate), candidate) > (len(best), best): best = candidateprint(len(best))Ответ: 18
Корректное выражение в короткой строке
В 5+*12+0*07+3 найди самое длинное корректное выражение с неотрицательными числами без ведущих нулей и операциями +, *.
Введите число.
Подсказка
Число — 0 или цифра 1–9 и любые цифры; затем повторения «знак, число».
Решение
6: 12+0*0 — после знака * число 07 с ведущим нулём, поэтому выражение заканчивается нулём.
Решение программой
import re s = '5+*12+0*07+3'number = r'(?:0|[1-9][0-9]*)'pattern = number + r'(?:[+*]' + number + r')*'print(max(len(m.group()) for m in re.finditer(pattern, s)))Ответ: 6
Натуральные числа, − и *
Текстовый файл 24-7.txt состоит из десятичных цифр и знаков «−» и «*». Определите максимальное количество символов в непрерывной последовательности, которая является корректным арифметическим выражением с натуральными числами без ведущих нулей: числа 0 в выражении нет, цифра 0 встречается только внутри чисел. Никакие два знака не стоят рядом.
Введите число.
Подсказка
Числа 0 нет: число — [1-9][0-9]*; знаки — [-*].
Решение
147: число — [1-9][0-9]*, знаки — [-*].
Решение программой
import re s = open('24-7.txt').readline().strip() number = r'[1-9][0-9]*'pattern = number + r'(?:[-*]' + number + r')*'print(max(len(m.group()) for m in re.finditer(pattern, s)))Ответ: 147
Выражение с + и *
Задача темы. Текстовый файл 24-6.txt состоит из десятичных цифр и знаков «+» и «*» (сложения и умножения). Определите максимальное количество символов в непрерывной последовательности, являющейся корректным арифметическим выражением с целыми неотрицательными числами (без знака). В этом выражении никакие два знака арифметических операций не стоят рядом, в записи чисел отсутствуют незначащие (ведущие) нули. В ответе укажите количество символов.
Введите число.
Подсказка
re.finditer по шаблону «число, затем (знак, число)*».
Решение
172.
Решение программой
import re s = open('24-6.txt').readline().strip() number = r'(?:0|[1-9][0-9]*)'pattern = number + r'(?:[+*]' + number + r')*'print(max(len(m.group()) for m in re.finditer(pattern, s)))Ответ: 172