August 31, 2021

Задание 24

Каналы авторов Физмат на сотку, Я обязательно сдам ЕГЭ по ИНФОРМАТИКЕ
Сложность: повышенный уровень
Время на решение одной задачи: около 15 минут
Начальные знания: основы python, генераторы массивов
Статьи по другим заданиям: тык
Другая статья по этому заданию: тык

Что из себя представляет задание?

Данная задача проверяет умение работать со строками, находить наибольшую (наименьшую) подпоследовательность

1) Задание на одну строку

Давайте рассмотрим задание из демоверсии 2021 года.

Текст задачи:

Текстовый файл состоит не более чем из 106 символов X, Y и Z. Определите максимальное количество идущих подряд символов, среди которых каждые два соседних различны. Для выполнения этого задания следует написать программу.

Приступим!

Откроем файл, приложенный к этому заданию

f = open('24.txt')

Считаем в переменную s первую строку из файла (в нашем случае единственную)

s = f.readline()

Давайте заведём счётчик количества подходящих элементов k и переменную для хранения длины максимальной подходящей подпоследовательности m

k = 1
m = 0

Запустим цикл for длины строки s, с помощью функции len(s). Стоит помнить, что мы будем проверять сразу два элемента, поэтому от len(s) надо отнять 1

for i in range(len(s)-1):

Давайте будем делать проверку, если s[i] не равно s[i+1], то к k будем прибавлять 1, иначе будем находить максимум из максимальной длины и новую длины с помощью функции max() и скидывать k до 1

    if s[i] != s[i+1]:
        k += 1
    else:
        m = max(m, k)
        k = 1

Осталось вывести переменную m

print(m)

Результат работы программы:

35

Ответ в этом задание 35

Программа полностью:

f = open('24.txt')
s = f.readline()
k = 1
m = 0

for i in range(len(s)-1):
    if s[i] != s[i+1]:
        k += 1

    else:
        m = max(m, k)
        k = 1

print(m)

Тонкости, которые могут помешать вам решать это задание:

1)Стоит помнить, когда мы считываем сразу два символа, следует задать начальное значение переменной k ровное 1, потому что длина подпоследовательности будет считаться без первого элемента

Разберём 24 задание из демоверсии 2022 года:

Текстовый файл состоит из символов P, Q, R и S. Определите максимальное количество идущих подряд символов в прилагаемом файле, среди которых нет идущих подряд символов P. Для выполнения этого задания следует написать программу.

Откроем файл и считаем все символы строки в переменную s. Также заведём переменные k и m для подсчёта количества и максимальной длины подходящей строки соответственно

f = open('24.txt')
s = f.readline()
k = 1
m = 0

Давайте будем проверять если s[i] равно P и s[i+1] равно P, то только тогда будем сравнивать наш результат с максимальной длиной, иначе будем прибавлять к k 1

for i in range(len(s)-1):
    if s[i] == 'P' and s[i+1] == 'P':
        m = max(m, k)
        k = 1
    else:
        k += 1

Осталось вывести m

print(m)

Результат работы программы:

188

Программа полностью:

f = open('24.txt')
s = f.readline()
k = 1
m = 0
for i in range(len(s)-1):
    if s[i] == 'P' and s[i+1] == 'P':
        m = max(m, k)
        k = 1
    else:
        k += 1

print(m)

Разберём возможную ошибку, которая может возникнуть при решении этого задания:

Давайте найдём ошибку в этой программе:

f = open('24.txt')
s = f.readline()
k = 1
m = 0
for i in range(len(s)-1):
    if s[i] != 'P' and s[i+1] != 'P':
        k += 1
    else:
        m = max(m, k)
        k = 1
print(m)

Давайте рассмотрим работу оператора and. Сначала он вычисляет значение слева, и если оно ложно, то уже не вычисляет значение справа. Поэтому когда нам встречается одиночная P, то длина будет сброшена.

Поэтому, используя эту программу, мы получим неправильный ответ.

Чтобы этот способ работал корректно, надо видоизменить программу:

f = open('24.txt')
s = f.readline()
k = 1
m = 0
for i in range(len(s)-1):
    if s[i] == 'P':
        if s[i+1] == 'P':
            m = max(m, k)
            k = 1
        else:
            k += 1
    else:
        k += 1
print(m)

Теперь если элемент равен P, то проверяется условие для следующего элемента.

Разберём такую вариацию 24 задания.

Текст задачи:

Задача взята с сайта К. Ю Полякова

Разберём решение этой задачи 3 способами:

Начнём с самого очевидного и надёжного способа:

f = open('24-j5.txt')
s = f.readline()
k = 0

for i in range(len(s)-2):
    if s[i] == 'K' and s[i+1] == 'O' and s[i+2] == 'T':
        k += 1

print(k)
    

Результат работы программы:

8192

Думаю, не стоит останавливаться на работе этого алгоритма.

Но что если я скажу вам, что в python есть функция, которая заменяет этот алгоритм одной строчкой. Да такая функция есть, и это функция count()

Давайте рассмотрим решение этой задачи с помощь функции count().

f = open('24-j5.txt')
s = f.readline()
print(s.count('KOT'))

Как можно заметить, функция count() находит количество всех совпадений в строке s, в данном случае строку 'KOT'.

Стоит заметить, что у функции count() есть один большой минус.

Давайте вызовем функцию count() от такой строки s

s = 'OTOTOACDF'
print(s.count('OTO'))

Получим такой результат работы:

1

На самом деле количество подходящих строк равно 2. Разберёмся, почему возвращаемый ответ некорректен. На самом деле функция count() считает непересекающиеся совпадения, поэтому когда часть слова может являться началом нового, то лучше пользоваться стандартными методами

Рассмотрим ещё один способ решения этого задания с помощью регулярных выражений.

Регулярные выражения:

Чтобы понять этот способ решения, немного теории. Для использования регулярных выражений нужно подключить библиотеку re

import re

Для решения этого задания нам понадобиться функция re.findall(), которая принимает первым аргументом регулярное выражение, вторым строку, в которой будет произведён поиск, а возвращает массив из подходящих элементов.

Например, мы хотим найти количество символов A в строке 'ACBFKADHBAAA'

Для этого достаточно в регулярном выражении указать символ A.

s = 'ACBFKADHBAAA'
result = re.findall('A', s)

Теперь в result записан массив, состоящий из элементов A. Для наглядности выведем result

print(result)

Результат работы программы:

['A', 'A', 'A', 'A', 'A']

Чтобы найти количество возьмём длину массива result с помощью функции len()

print(len(result))

Результат работы программы:

5

Но, что если мы хотим найти длину последовательности, состоящих минимум из двух элементов A? Для этого достаточно в регулярном выражении после A указать {2,}, это означает, что символ A должен встречаться в строке минимум 2 раза.

s = 'ACBFKADHBAAA'
result = re.findall('A{2,}', s)
print(result)

Результат работы программы:

['AAA']

Если мы хотим указать лимит повторений, то достаточно указать его вторым аргументом в фигурных скобках:

s = 'ACBFKADHBAAA'
result = re.findall('A{2,3}', s)
print(result)

Результат работы программы:

['AAA']

Больше про работу регулярных выражений тык

Вернёмся к нашей задаче. Для её решение необходимо в регулярном выражении функции re.fndall() указать строку KOT

import re
f = open('24-j5.txt')
s = f.readline()

result = re.findall('KOT', s)
print(len(result))

Результат работы программы:

8192

Как и с функцией count() у регулярных выражений есть проблема с поиском подстрок, которые могут являться началом следующей подстроки.

Регулярные выражения помогают укротить код в несколько раз, но далеко не все задачки можно решить, используя их, поэтому не стоит смотреть на регулярные выражения как на панацею.

Давайте разберём задание, которое с помощью регулярных выражений уничтожается в несколько строк.

Текст задачи:

Задача взята с сайта К. Ю. Полякова

Решим эту задачу стандартным способом:

Пройдём циклом for по всей строке s. Если 3 подряд идущих символов равно KOT, то будем прибавлять к счётчику k единицу, иначе сбрасывать k до 2

f = open('24-j5.txt')
s = f.readline()
k = 2
for i in range(len(s)-2):
    if s[i] == 'K' and s[i+1] == 'O' and s[i+2] == 'T':
        k += 1
    else:
        k = 2

print(k)
    

Результат работы программы:

2

Но давайте решим это с помощью регулярных выражений. Для этого нам следует в регулярном выражении после комбинации KOT указать число повторений {1,}(минимум 1) и взять KOT в скобки.

Давайте проведём тест на пробной строке

import re
s = 'KOTHHDDKOTKOT'
result = re.findall('(KOT){1,}', s)
print(result)

Результат работы программы:

['KOT', 'KOT']

Как можно заметить это не то, что нам нужно. Программа нашла количество подходящих строк, но вывела только искомую строку. Для того, чтобы избежать эту проблему, надо взять всё регулярное выражение в скобки. Тогда программа вернёт в виде кортежа найденную подпоследовательность и искомый строку.

import re
s = 'KOTHHDDKOTKOT'
result = re.findall('((KOT){1,})', s)
print(result)

Результат работы программы:

[('KOT', 'KOT'), ('KOTKOT', 'KOT')]

Чтобы получить нужную нам подстроку, надо вывести нулевой элемент каждого кортежа. Это легко сделать с помощью генератора массива:

print([i[0] for i in result])

Результат работы программы:

['KOT', 'KOTKOT']

Теперь можно использовать наш файл. Чтобы найти самую длинную подпоследовательность, можно использовать функцию max().

import re
f = open('24-j5.txt')
s = f.readline()
result = re.findall('((KOT){1,})', s)
print(max([i[0] for i in result]))

Результат работы программы:

KOTKOT

Как можно заметить, самая большая последовательность подряд идущих комбинаций KOT равна 2.

1) Задание на множество строк

После хорошей практики на одну строку можно приступить к множеству строк.

Давайте научимся обрабатывать сразу несколько строк на данном примере.

Текст задачи:

Задача взята с сайта К. Ю. Полякова

Как обычно откроем приложенный файл

f = open('24-s1.txt')

Также заведём переменную k, для подсчёта количества подходящих строк

k = 0

Чтобы считывать строку за строкой, запустим цикл for по всему файлу, который записан в переменную f.

for s in f:

Теперь на каждой итерации в переменную s будет записываться новая строка. Когда мы научились как считывать строку за строкой, нет никаких трудностей решить это задание.

Давайте решим это задание 3 способами:

1) Стандартный

f = open('24-s1.txt')
k = 0

for s in f:
    count_x = 0
    count_s = 0
    for i in range((len(s))):
        if s[i] == 'X':
            count_x += 1
        if s[i] == 'S':
            count_s += 1

    if count_x == count_s:
        k += 1
            
print(k)

Счётчики count_x и count_s считают количество повторений символов X и S соответственно. С помощью второго цикла будем ввести подсчёты повторений. После этого будет сравнивать count_x и count_s. Если они равны, то будем прибавлять к k единицу.

Результат работы программы:

48

2) С помощью регулярных выражений

import re
f = open('24-s1.txt')
k = 0

for s in f:
    if len(re.findall('X', s)) == len(re.findall('S', s)):
        k += 1
print(k)

В данном случае нам не надо запускать второй цикл, достаточно сравнить длины найденных массивов. В этом случае мы получим такой же ответ

Результат работы программы:

48

3) С помощью функции count()

f = open('24-s1.txt')
k = 0

for s in f:
    if s.count('X') == s.count('S'):
        k += 1
print(k)

Как можно заметить, данный способ не сильно отличается от способа с регулярными выражениями.

Результат работы программы:

48

Рассмотрим задание, в котором раскрывается вся прелесть регулярных выражений.

Текст задачи:

Задача взята с сайта К. Ю. Полякова

Для начала решим данное задание стандартным способ:

f = open('24-s1.txt')
k = 0

for s in f:
    for i in range(len(s)-2):
        if s[i] == 'F' and s[i+2] == 'O':
            k += 1
            break
print(k)

В данном случае если попадается [i]-ый элемент равен F, а [i+1]-ый равен равен O, то к k будем прибавлять единицу и "ломать" второй цикл, так как нам важно наличие этой комбинации в строке и неважно количество.

Результат работы программы:

757

Давайте рассмотрим решение через регулярные выражения:

import re
f = open('24-s1.txt')
k = 0

for s in f:
    if re.findall('F.O', s):
        k += 1

print(k)

Чтобы обозначить один произвольный символ в регулярном выражении, надо поставить точку.

Результат работы программы:

757

Интересные и сложные задания, которые стоит решить для понимания темы

Текст задачи:

Задача взята с сайта Решу ЕГЭ

Данную задачу легче решить на pascal, но мы решим её на python. Для начала всё стандартно. Заведём переменную m, которая будет хранить длину самой большой последовательности между одинаковыми буквами. Будем проходить по каждой строке, в которой символ G встречается меньше 25 раз.

f = open('inf_26_04_21_24.txt')
m = 0
for s in f:
    if s.count('G') < 25:

Но как нам получить доступ к английскому алфавиту в виде массива или цикла, чтобы было удобно работать. В pascal сделать это легко, но мы не ищем лёгких путей (поэтому мы выбрали python). Чтобы получить ascii код буквы, в python есть функция ord(), которая поможет нам перебрать весь английский алфавит.

for s in f:
    if s.count('G') < 25:
        for i in range(ord('A'), ord('Z')+1):

В pascal есть функции IndexOf() и LastIndexOf(), которые определяют первую и последнюю позицию элемента в строке. В python, как вы могли понять, нет подобного, поэтому создадим что-то похожее сами. Заведём переменные op и end для определения начальной и конечной позиции элемента соответственно. Также запустим третий цикл, который будет проходить по строке s. Каждый раз, когда будем встречать символ chr(i), будем обновлять op и end с помощью функций min() и max().

for s in f:
    if s.count('G') < 25:
        for i in range(ord('A'), ord('Z')+1):
            op = 10**6
            end = 0
            for j in range(len(s)):
                if s[j] == chr(i):
                    op = min(op, j)
                    end = max(end, j)

Теперь разность переменных op и end будет самая длинная последовательность между одинаковыми буквами. Осталось только сравнивать с самой большой длинной.

for s in f:
    if s.count('G') < 25:
        for i in range(ord('A'), ord('Z')+1):
            op = 10**6
            end = 0
            for j in range(len(s)):
                if s[j] == chr(i):
                    op = min(op, j)
                    end = max(end, j)
            m = max(m, end-op)

Осталось вывести ответ.

Результат работы программы:

1001

Подведём итоги

Мы рассмотрели 24 задачи как на одну строку, так и множество.

Изучили частично регулярные выражения, с помощью которых можно в несколько раз уменьшить код.

каналы авторов Физмат на сотку, Я обязательно сдам ЕГЭ по ИНФОРМАТИКЕ
Статьи по другим заданиям: тык