Подтема 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)Пройди строку BABAACB по шагам. Самый важный момент — третья A на позиции 4: окно BABAA плохое. Убираем B — в ABAA всё ещё три A. Убираем A — в BAA их две. Поэтому нужен , а не if: иногда убрать надо несколько символов.
- До цикла: окно пустое, left = 0, best = 0.
- right = 0: окно s[0:1] = 'B', вхождений A: 0, длина 1, best = 1.
- right = 1: окно s[0:2] = 'BA', вхождений A: 1, длина 2, best = 2.
- right = 2: окно s[0:3] = 'BAB', вхождений A: 1, длина 3, best = 3.
- right = 3: окно s[0:4] = 'BABA', вхождений A: 2, длина 4, best = 4.
- right = 4: добавили 'A', вхождений A стало 3 > 2 — окно плохое, двигаем левую границу.
- Убрали s[0] = 'B': теперь left = 1, вхождений A в окне: 3.
- Убрали s[1] = 'A': теперь left = 2, вхождений A в окне: 2.
- right = 4: окно s[2:5] = 'BAA', вхождений A: 2, длина 3, best = 4.
- right = 5: окно s[2:6] = 'BAAC', вхождений A: 2, длина 4, best = 4.
- 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)На экзамене 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, фрагмент 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)Пройди по шагам: на позиции 5 появляется пара TT, которая перекрывается с парой 3–4. Пар стало две, и левая граница уходит за T на позиции 3.
- До цикла: окно пустое, left = 0, best = 0.
- right = 0: окно s[0:1] = 'T', вхождений TT: 0, длина 1, best = 1.
- right = 1: окно s[0:2] = 'TT', вхождений TT: 1, длина 2, best = 2.
- right = 2: окно s[0:3] = 'TTA', вхождений TT: 1, длина 3, best = 3.
- right = 3: окно s[0:4] = 'TTAT', вхождений TT: 1, длина 4, best = 4.
- right = 4: добавили 'T', вхождений TT стало 2 > 1 — окно плохое, двигаем левую границу.
- Убрали s[0] = 'T': теперь left = 1, вхождений TT в окне: 1.
- right = 4: окно s[1:5] = 'TATT', вхождений TT: 1, длина 4, best = 4.
- right = 5: добавили 'T', вхождений TT стало 2 > 1 — окно плохое, двигаем левую границу.
- Убрали s[1] = 'T': теперь left = 2, вхождений TT в окне: 2.
- Убрали s[2] = 'A': теперь left = 3, вхождений TT в окне: 2.
- Убрали s[3] = 'T': теперь left = 4, вхождений TT в окне: 1.
- right = 5: окно s[4:6] = 'TT', вхождений TT: 1, длина 2, best = 4.
- 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)
Проверьте себя
Ответь сначала сам, потом открой ответ.
while, а не if?ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
if count == K. Сжатие окна то же.ОтветСкрыть ответ
Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Не более двух A
В строке ABCABBACB найди максимальную длину фрагмента с не более чем двумя A.
Введите число.
Подсказка
Когда A стало три, сдвигай left, пока одна A не уйдёт из окна.
Решение
8: BCABBACB с позиции 1 — в нём A на позициях 3 и 6.
Решение программой
s = 'ABCABBACB'left = count = best = 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)Ответ: 8
Не более 12 букв Q
Текстовый файл 24-4.txt содержит заглавные латинские буквы. Определите максимальное количество идущих подряд символов, среди которых буква Q встречается не более 12 раз.
Введите число.
Подсказка
Скользящее окно со счётчиком Q.
Решение
5647.
Решение программой
s = open('24-4.txt').readline().strip() left = count = best = 0for right in range(len(s)): if s[right] == 'Q': count += 1 while count > 12: if s[left] == 'Q': count -= 1 left += 1 best = max(best, right - left + 1)print(best)Ответ: 5647
Ровно две A
В строке ABCABBACB найди максимальную длину фрагмента, в котором ровно две буквы A.
Введите число.
Подсказка
То же окно, но ответ обновляется только при count == 2.
Решение
8: тот же фрагмент — в самом широком окне ровно две A.
Решение программой
s = 'ABCABBACB'left = count = best = 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)Ответ: 8
Буква W ровно 100 раз
Текстовый файл 24-4.txt состоит из заглавных букв латинского алфавита. Определите максимальное количество идущих подряд символов, среди которых буква W встречается ровно 100 раз.
Введите число.
Подсказка
Окно сжимается, пока W больше 100, а ответ обновляется только при ровно 100.
Решение
3677.
Решение программой
s = open('24-4.txt').readline().strip()left = 0count = 0best = 0for right in range(len(s)): if s[right] == 'W': count += 1 while count > 100: if s[left] == 'W': count -= 1 left += 1 if count == 100: best = max(best, right - left + 1) print(best)Ответ: 3677
A и B не более одного раза
В строке ABCBADAB найди самый длинный фрагмент, в котором каждая из букв A и B встречается не более одного раза.
Введите число.
Подсказка
Два счётчика; окно сжимается, пока превышен счётчик только что добавленной буквы.
Решение
4: CBAD с позиции 2.
Решение программой
s = 'ABCBADAB'count = {'A': 0, 'B': 0}left = best = 0for right in range(len(s)): c = s[right] if c in count: count[c] += 1 while count[c] > 1: if s[left] in count: count[s[left]] -= 1 left += 1 best = max(best, right - left + 1)print(best)Ответ: 4
J, K, X, Z не более 6 раз
Для файла 24-4.txt определите максимальное количество идущих подряд символов, среди которых каждая из букв J, K, X, Z встречается не более 6 раз.
Введите число.
Подсказка
На каждую букву свой счётчик в словаре.
Решение
797: на каждую букву свой счётчик в словаре.
Решение программой
s = open('24-4.txt').readline().strip() count = {c: 0 for c in 'JKXZ'}left = best = 0for right in range(len(s)): c = s[right] if c in count: count[c] += 1 while count[c] > 6: if s[left] in count: count[s[left]] -= 1 left += 1 best = max(best, right - left + 1)print(best)Ответ: 797
По алфавиту и не больше одной гласной
Текстовый файл 24-4.txt состоит из заглавных букв латинского алфавита. Определите длину самой длинной последовательности идущих подряд символов, в которой символы идут в алфавитном порядке и находится не более одной гласной (A, E, I, O, U, Y).
Введите число.
Подсказка
Нарушен порядок — окно начинается заново с текущей буквы; лишняя гласная — левая граница догоняет.
Решение
8.
Решение программой
s = open('24-4.txt').readline().strip()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)Ответ: 8
Не более одной пары AA
В строке AAABAAB найди самый длинный фрагмент, в котором не более одной пары AA (пары могут перекрываться).
Введите число.
Подсказка
Пара считается в окне, только когда оба её символа внутри.
Решение
5: ABAAB с позиции 2 — в нём одна пара AA; в AAA пар две.
Решение программой
s = 'AAABAAB'left = count = best = 0for right in range(len(s)): if right >= 1 and s[right - 1:right + 1] == 'AA': if right - 1 >= left: count += 1 while count > 1: if s[left:left + 2] == 'AA' and left + 1 <= right: count -= 1 left += 1 best = max(best, right - left + 1)print(best)Ответ: 5
Ровно 25 пар TT
Для файла 24-4.txt определите максимальное количество идущих подряд символов, среди которых пара букв TT встречается ровно 25 раз. Пары могут перекрываться: в TTT две пары TT.
Введите число.
Подсказка
Пары перекрываются; учитывай пару, только когда оба символа в окне.
Решение
29516: пара учитывается, только когда оба её символа внутри окна; count('TT') перекрытия не считает.
Решение программой
s = open('24-4.txt').readline().strip() left = count = best = 0for right in range(len(s)): if right >= 1 and s[right - 1:right + 1] == 'TT': if right - 1 >= left: count += 1 while count > 25: if s[left:left + 2] == 'TT' and left + 1 <= right: count -= 1 left += 1 if count == 25: best = max(best, right - left + 1)print(best)Ответ: 29516