К содержанию

Задание 24

Обработка символьной информации

Фрагменты строки: текущая длина цепочки, образцы и частоты, скользящее окно, позиции и границы, корректные выражения по шаблону.

7 подтем · 54 задачи + 12 дополнительных · высокий · ≈ 18 мин

Подтема 1 из 7

Строка и цепочка

Что спрашивают в задании 24, как прочитать строку из файла и найти самую длинную цепочку за один проход.

Как устроена тема

В задании 24 дают файл — одну длинную строку примерно из миллиона символов. Нужно найти в ней самый длинный (иногда самый короткий) кусок, идущий подряд, с заданным свойством. Вот задача, которую мы решим в этой теме:

«Текстовый файл 24-6.txt состоит из десятичных цифр и знаков «+» и «*» (сложения и умножения). Определите максимальное количество символов в непрерывной последовательности, являющейся корректным арифметическим выражением с целыми неотрицательными числами (без знака). В этом выражении никакие два знака арифметических операций не стоят рядом, в записи чисел отсутствуют незначащие (ведущие) нули. В ответе укажите количество символов.»

Перебрать все куски строки не получится — их сотни миллиардов. Поэтому строку проходят один раз и по дороге помнят только то, что нужно для ответа. Разберём, что значат слова условия:

В условииЧто это значит
текстовый файл из 10⁶ символоводна строка; кусков в ней около 5 · 10¹¹ — перебор не успеет
идущие подряд, непрерывная последовательность, подстрокакусок строки без пропусков: символы пропускать и переставлять нельзя
максимальное количество символовдлина самого длинного подходящего куска
корректное арифметическое выражениечисло, знак, число, знак, …, число — ни двух знаков рядом, ни знака на краю
нет незначащих (ведущих) нулейчисло не начинается с 0, кроме самого числа 0
если таких несколько, …правило выбора при равной длине — его выполняют дословно

Начнём с простых цепочек — на них видна главная идея одного прохода, — потом дойдём до окон, позиций и выражений. Тема состоит из шести подтем и дополнительного урока — у каждой своя страница:

  1. Строка и цепочка — ты сейчас здесь: чтение файла и самая длинная цепочка.
  2. Условия на соседей — одинаковые подряд, соседи различны, запретные пары.
  3. Образцы и блоки — DOGDOG…, цепочки пар, палиндромы.
  4. Скользящее окно — не более K и ровно K букв во фрагменте.
  5. Позиции и границы — K подряд идущих вхождений, особые начало и конец.
  6. Числа и выражения — регулярные выражения; решим задачу темы: ответ 172.
  7. Выражения с условиями и баланс — дополнительный урок для сложных вариаций.

Подряд — без пропусков

Что значит «идущие подряд символы»?

Кусок строки без пропусков: символы берут один за другим, ничего не выкидывая из середины. В строке AAAXAAAA самая длинная цепочка букв A — четыре, а не семь: чтобы получить семь, пришлось бы выкинуть X. Такой кусок ещё называют подстрокой или фрагментом.

В Python к символу обращаются по номеру — индексу. Счёт идёт с нуля: s[0] — первый символ, s[-1] — последний, len(s) — длина. Кусок вырезают s[a:b]: от a включительно до b не включительно.

s = 'CABAC'print(s[0], s[4], s[-1], len(s))print(s[1:4])
C C C 5 ABA

Срез s[1:4] взял символы с номерами 1, 2 и 3 — правая граница не входит:

Срез s[1:4] — символы 1, 2, 3; длина 4 − 1 = 3. Под строкой — отрицательные индексы: s[-1] — последний символ.

Если кусок задан двумя включёнными концами left и right, его длина — right - left + 1: символы с номерами 3, 4, 5 и 6 — это четыре символа, хотя 6 − 3 = 3.

Чтение файла

Как прочитать строку из файла?

Одной строкой: s = open('24.txt').readline().strip(). Без strip() в конце останется перевод строки — лишний символ. читает первую строку файла вместе с невидимым символом перевода строки, убирает его. В учебном файле 24.txt одна строка — XXAXXXXBXX:

s = open('24.txt').readline().strip()print(s, len(s)) line = open('24.txt').readline()print(len(line), len(line.strip()))
XXAXXXXBXX 10 11 10

С переводом строки символов 11, без него — 10. Если ищем «символы, отличные от A», перевод строки тоже посчитается — и ответ станет на единицу больше. Поэтому strip() пишем всегда.

Тонкости:[:-1] и файлы из многих строк

readline()[:-1] отрезает последний символ, даже если перевода строки в файле нет, — тогда пропадёт настоящий символ данных. strip() убирает только пробелы и переводы строк, а в файлах задания 24 их внутри строки не бывает.

Если в файле много независимых строк, их перебирают циклом for line in open('24.txt') и каждую очищают strip() отдельно.

Текущая и лучшая длина

Как найти самую длинную цепочку букв X, не перебирая все куски строки?

Идти по строке и помнить два числа: current — длину цепочки, которая кончается на текущем символе, и best — лучшую длину из уже виденных. Подходящий символ продлевает цепочку на единицу, неподходящий обрывает её: current = 0. После каждого символа best сравнивается с current.

s = open('24.txt').readline().strip() current = 0best = 0for c in s:    if c == 'X':        current += 1    else:        current = 0    best = max(best, current) print(best)
4

Пройди строку XXAXXXXBXX по шагам: заливка — текущая цепочка, скобка — лучшая из найденных.

  1. До цикла: current = 0, best = 0.
  2. s[0] = 'X': 'X' подходит: началась новая цепочка. current = 1, best = 1 — новый лучший результат.
  3. s[1] = 'X': 'X' подходит: цепочка продолжилась. current = 2, best = 2 — новый лучший результат.
  4. s[2] = 'A': 'A' не подходит и не входит ни в одну цепочку: сброс до 0. current = 0, best = 2.
  5. s[3] = 'X': 'X' подходит: началась новая цепочка. current = 1, best = 2.
  6. s[4] = 'X': 'X' подходит: цепочка продолжилась. current = 2, best = 2.
  7. s[5] = 'X': 'X' подходит: цепочка продолжилась. current = 3, best = 3 — новый лучший результат.
  8. s[6] = 'X': 'X' подходит: цепочка продолжилась. current = 4, best = 4 — новый лучший результат.
  9. s[7] = 'B': 'B' не подходит и не входит ни в одну цепочку: сброс до 0. current = 0, best = 4.
  10. s[8] = 'X': 'X' подходит: началась новая цепочка. current = 1, best = 4.
  11. s[9] = 'X': 'X' подходит: цепочка продолжилась. current = 2, best = 4.

Сравнивать best с current нужно на каждом шаге, а не только при обрыве. Иначе цепочка в самом конце строки в ответ не попадёт: после неё обрыва уже не будет. Строка проходится один раз — миллион символов программа обработает за доли секунды.

Что выводить

В условии просят не длину самой длинной цепочки X, а номер её первого символа, а если таких цепочек несколько — последней из них. Что меняется?

Меняется только то, что запоминаем: вместе с длиной храним начало лучшей цепочки, а правило «при равенстве» записываем явно. Перед кодом ответь на три вопроса: какие куски подходят, что сравниваем, что выводим.

s = 'XXAXXBXXAX'current = 0best_length = 0best_start = 0for i in range(len(s)):    if s[i] == 'X':        current += 1    else:        current = 0    if current > 0 and current >= best_length:        best_length = current        best_start = i - current + 1 print(best_length, best_start)
2 6

Цепочка, которая кончается на символе i, начинается на i - current + 1. Проверка current > 0 не даёт «цепочке» нулевой длины на символе A занять место ответа. Три цепочки длины 2 начинаются с 0-го, 3-го и 6-го символа. Знак >= пускает более позднюю цепочку при равенстве, поэтому выбрана последняя. Для первой из равных нужен строгий >.

Нумерацию берут из условия: если символы считают с единицы, к индексу Python прибавляют 1.

Итого

  • Подрядкусок без пропусков — подстрока
  • Чтение файлаs = open('24.txt').readline().strip()
  • Длина кускаright − left + 1 при включённых концах
  • currentдлина цепочки, которая кончается на текущем символе
  • bestобновлять после каждого символа
  • При равенстве«>=» — последний из равных, «>» — первый

Проверьте себя

Ответь сначала сам, потом открой ответ.

Вопрос 1
Для s = 'QWERTY' чему равны s[1], s[-1], s[2:5] и len(s)?
ОтветСкрыть ответ
Ответ
W, Y, ERT и 6: срез s[2:5] берёт символы с номерами 2, 3 и 4.
Вопрос 2
В строке BBBABBBBAB найди самую длинную цепочку букв B, идущих подряд. Почему ответ не 7?
ОтветСкрыть ответ
Ответ
4: цепочка BBBB с 4-го по 7-й символ. Семь букв B получились бы только с пропуском A, а «подряд» пропусков не допускает.
Вопрос 3
Файл — одна строка XYZX с переводом строки в конце. Сколько символов, отличных от X, насчитает программа, если забыть strip()?
ОтветСкрыть ответ
Ответ
3 вместо 2: перевод строки — тоже символ, и он не X.
Вопрос 4
Программа обновляет best только тогда, когда цепочка обрывается. Что она напечатает для XXAXXX, если ищем цепочку X?
ОтветСкрыть ответ
Ответ
2 вместо 3: последняя цепочка XXX не попала в ответ — после неё обрыва уже не было.
Вопрос 5
«Найдите самый длинный фрагмент без буквы Q; если таких несколько, укажите номер первого символа последнего из них». Что здесь сравниваем и что выводим?
ОтветСкрыть ответ
Ответ
Сравниваем длину, при равной длине берём фрагмент правее, а выводим номер его первого символа, а не длину.

Практика

Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.

Сколько всего подстрок

Сколько непустых подстрок у строки из 8 символов? А у строки из 300 000 символов?

Введите два числа через пробел в том порядке, в каком их спрашивают.

Длина по включённым границам

Фрагмент начинается на позиции left = 4 и заканчивается на позиции right = 9, обе включены. Найди его длину, количество символов строго между концами и разность номеров.

Введите три числа через пробел: длину, количество символов строго между концами и разность номеров.

K чаще, чем L

Файл 24-3.txt содержит строки из заглавных латинских букв. Определите количество строк, в которых буква K встречается больше раз, чем буква L.

24-3.txtСтроки из заглавных латинских буквТекстовый файл · 200,2 КБ

Введите число.

В строке AXXXBXXXXXAX найди самую длинную цепочку X.

Введите число.

Текстовый файл 24-1.txt состоит из символов X, Y и Z. Определите максимальное количество идущих подряд символов, среди которых нет буквы Y.

24-1.txtСтрока из символов X, Y и ZТекстовый файл · 195,3 КБ

Введите число.

В строке ABYCDXEFGHYXI найди самый длинный фрагмент без букв X и Y.

Введите число.

Самая правая из длинных

В строке YXXYXXYXX найди номер первого символа самой длинной цепочки X; если таких несколько — самой правой. Нумерация с нуля. Каким будет ответ при нумерации с единицы?

Введите два числа через пробел: номер при нумерации с нуля, затем с единицы.