К содержанию

Подтема 4 из 7

Скользящее окно

Две границы фрагмента: не более K и ровно K букв, несколько условий сразу, пары букв внутри окна.

Две границы

Как найти самый длинный фрагмент, в котором не больше двух букв A? Цепочка здесь не поможет: одна лишняя A не обнуляет всё, что было до неё.

Хранить две границы фрагмента — окна. Правая идёт по строке по одному символу. Когда букв A стало больше двух, левая сдвигается вправо, пока лишняя A не уйдёт из окна. Количество A в окне хранится в счётчике: правая граница добавляет символ, левая убирает.

s = 'BABAACB'left = 0count = 0best = 0for right in range(len(s)):    if s[right] == 'A':        count += 1    while count > 2:        if s[left] == 'A':            count -= 1        left += 1    best = max(best, right - left + 1) print(best)
5

Пройди строку BABAACB по шагам. Самый важный момент — третья A на позиции 4: окно BABAA плохое. Убираем B — в ABAA всё ещё три A. Убираем A — в BAA их две. Поэтому нужен , а не if: иногда убрать надо несколько символов.

  1. До цикла: окно пустое, left = 0, best = 0.
  2. right = 0: окно s[0:1] = 'B', вхождений A: 0, длина 1, best = 1.
  3. right = 1: окно s[0:2] = 'BA', вхождений A: 1, длина 2, best = 2.
  4. right = 2: окно s[0:3] = 'BAB', вхождений A: 1, длина 3, best = 3.
  5. right = 3: окно s[0:4] = 'BABA', вхождений A: 2, длина 4, best = 4.
  6. right = 4: добавили 'A', вхождений A стало 3 > 2 — окно плохое, двигаем левую границу.
  7. Убрали s[0] = 'B': теперь left = 1, вхождений A в окне: 3.
  8. Убрали s[1] = 'A': теперь left = 2, вхождений A в окне: 2.
  9. right = 4: окно s[2:5] = 'BAA', вхождений A: 2, длина 3, best = 4.
  10. right = 5: окно s[2:6] = 'BAAC', вхождений A: 2, длина 4, best = 4.
  11. right = 6: окно s[2:7] = 'BAACB', вхождений A: 2, длина 5, best = 5.

Ответ — 5, окно BAACB. Вложенный цикл не делает программу медленной: left никогда не идёт назад, поэтому за весь проход он сдвинется не больше len(s) раз. Миллион символов — около двух миллионов шагов.

Почему окно ничего не теряет

Если в окне уже три A, расширение вправо их не уберёт: такое начало больше не пригодится, и его можно отбросить. После сдвига left стоит так далеко слева, как только можно для текущего правого конца. Значит, для каждого right найдено самое длинное подходящее окно, а среди них — и ответ.

Ровно K

В условии: «буква T встречается ровно 100 раз». Что поменять в окне?

Окно то же — левая граница двигается, пока букв больше K. А ответ обновляется, только когда их ровно K. Меньше K — фрагмент ещё не подходит, а сужение окна букв не прибавит.

s = 'BABAACB'left = 0count = 0best = 0for right in range(len(s)):    if s[right] == 'A':        count += 1    while count > 2:        if s[left] == 'A':            count -= 1        left += 1    if count == 2:        best = max(best, right - left + 1) print(best)
5

На экзамене K = 100, а файл — миллион символов: программа та же, меняется только число. Если в строке меньше K нужных букв, best останется 0 — подходящего фрагмента нет.

Несколько условий

Нужен самый длинный фрагмент, где буквы идут в алфавитном порядке и гласных не больше одной. Как совместить два условия?

Каждое условие — своим приёмом. Нарушение порядка — условие на соседей: окно начинается заново с текущей буквы. Лишняя гласная — ограничение на количество: левая граница догоняет.

s = 'AEKZIOPSW'vowels = 'AEIOUY'left = 0count = 0best = 0for right in range(len(s)):    if right > 0 and s[right] < s[right - 1]:        left = right        count = 0    if s[right] in vowels:        count += 1    while count > 1:        if s[left] in vowels:            count -= 1        left += 1    best = max(best, right - left + 1) print(best)
4

Ответ — 4, фрагмент OPSW. EKZ тоже по алфавиту, но короче, а после Z порядок рушится: I меньше Z.

Если ограничение на каждую из нескольких букв — «J, K, X и Z не больше 6 раз каждая», — счётчики держат в , а окно сжимают, пока превышен счётчик только что добавленной буквы.

Пары в окне

Ограничение бывает на пары: «пара гласная + согласная встречается не больше 130 раз». Когда пара входит в окно и когда уходит?

Пара считается, только когда лежит в окне целиком. Она входит, когда правая граница добавляет её второй символ, и уходит, когда левая проходит её первый. Пары могут перекрываться: в TTT две пары TT. Пара, которая кончается на right, — это срез s[right - 1:right + 1], а пара, которая начинается на left, — s[left:left + 2].

s = 'TTATTTB'left = 0pairs = 0best = 0for right in range(len(s)):    if right > 0 and s[right - 1:right + 1] == 'TT':        pairs += 1    while pairs > 1:        if s[left:left + 2] == 'TT':            pairs -= 1        left += 1    best = max(best, right - left + 1) print(best)
4

Пройди по шагам: на позиции 5 появляется пара TT, которая перекрывается с парой 3–4. Пар стало две, и левая граница уходит за T на позиции 3.

  1. До цикла: окно пустое, left = 0, best = 0.
  2. right = 0: окно s[0:1] = 'T', вхождений TT: 0, длина 1, best = 1.
  3. right = 1: окно s[0:2] = 'TT', вхождений TT: 1, длина 2, best = 2.
  4. right = 2: окно s[0:3] = 'TTA', вхождений TT: 1, длина 3, best = 3.
  5. right = 3: окно s[0:4] = 'TTAT', вхождений TT: 1, длина 4, best = 4.
  6. right = 4: добавили 'T', вхождений TT стало 2 > 1 — окно плохое, двигаем левую границу.
  7. Убрали s[0] = 'T': теперь left = 1, вхождений TT в окне: 1.
  8. right = 4: окно s[1:5] = 'TATT', вхождений TT: 1, длина 4, best = 4.
  9. right = 5: добавили 'T', вхождений TT стало 2 > 1 — окно плохое, двигаем левую границу.
  10. Убрали s[1] = 'T': теперь left = 2, вхождений TT в окне: 2.
  11. Убрали s[2] = 'A': теперь left = 3, вхождений TT в окне: 2.
  12. Убрали s[3] = 'T': теперь left = 4, вхождений TT в окне: 1.
  13. right = 5: окно s[4:6] = 'TT', вхождений TT: 1, длина 2, best = 4.
  14. right = 6: окно s[4:7] = 'TTB', вхождений TT: 1, длина 3, best = 4.

Окна TTAT и TATT содержат по одной целой паре — ответ 4. Для пары «гласная + согласная» меняется только проверка: s[right - 1] in vowels and s[right] not in vowels.

Итого

  • Окноright идёт вперёд, left догоняет по while
  • Не более Kсжимать, пока count > K
  • Ровно Kответ — только при count == K
  • Порядок и количествонарушен порядок — left = right; лишняя буква — while
  • Парывходит со вторым символом, уходит с первым
  • Скоростьleft не идёт назад: O(n)

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

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

Вопрос 1
Почему для сдвига левой границы нужен while, а не if?
ОтветСкрыть ответ
Ответ
Иногда, чтобы убрать одну лишнюю A, приходится выкинуть несколько символов перед ней: в BABAA сначала уходит B, потом A.
Вопрос 2
В строке 300 000 символов. Сколько раз всего может сдвинуться left? Почему программа не делает 300 000² действий?
ОтветСкрыть ответ
Ответ
Не больше 300 000 раз: left не возвращается назад. Вместе с right — около 600 000 шагов.
Вопрос 3
Найди самый длинный фрагмент строки AABAB, в котором не больше одной A.
ОтветСкрыть ответ
Ответ
3: BAB. Окна AB и AB короче.
Вопрос 4
Чем программа для «ровно K букв» отличается от программы для «не более K»?
ОтветСкрыть ответ
Ответ
Только условием перед обновлением ответа: if count == K. Сжатие окна то же.
Вопрос 5
Сколько пар TT в окне TTTT? Пары могут перекрываться.
ОтветСкрыть ответ
Ответ
Три: с позиций 0, 1 и 2.

Практика

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

Не более двух A

В строке ABCABBACB найди максимальную длину фрагмента с не более чем двумя A.

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

Не более 12 букв Q

Текстовый файл 24-4.txt содержит заглавные латинские буквы. Определите максимальное количество идущих подряд символов, среди которых буква Q встречается не более 12 раз.

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

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

Ровно две A

В строке ABCABBACB найди максимальную длину фрагмента, в котором ровно две буквы A.

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

Буква W ровно 100 раз

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

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

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

A и B не более одного раза

В строке ABCBADAB найди самый длинный фрагмент, в котором каждая из букв A и B встречается не более одного раза.

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

J, K, X, Z не более 6 раз

Для файла 24-4.txt определите максимальное количество идущих подряд символов, среди которых каждая из букв J, K, X, Z встречается не более 6 раз.

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

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

По алфавиту и не больше одной гласной

Текстовый файл 24-4.txt состоит из заглавных букв латинского алфавита. Определите длину самой длинной последовательности идущих подряд символов, в которой символы идут в алфавитном порядке и находится не более одной гласной (A, E, I, O, U, Y).

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

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

Не более одной пары AA

В строке AAABAAB найди самый длинный фрагмент, в котором не более одной пары AA (пары могут перекрываться).

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

Ровно 25 пар TT

Для файла 24-4.txt определите максимальное количество идущих подряд символов, среди которых пара букв TT встречается ровно 25 раз. Пары могут перекрываться: в TTT две пары TT.

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

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