Чему научитесь
- Выбирать между перебором, методом, срезом и кодами по условию задачи
- Соединять приёмы модуля в одном решении
- Находить чужие ошибки и отличать их по симптому
- Замечать, когда верное решение не пройдёт по времени
Как устроено занятие
Сначала разбор с примерами и короткими проверками понимания — они нужны, чтобы поймать непонятое сразу, а не через три темы.
Дальше 15 задач лестницей: разминка, основа, со звёздочкой. Занятие засчитывается, когда решено 10 — остальные не пропадают и учитываются отдельно.
После занятия — вторая часть, ещё 15 задач на те же приёмы в новых сюжетах.
Сколько это займёт
Примерно час-полтора вместе с задачами. Сроков нет: можно закрыть вкладку и вернуться когда удобно — прогресс сохранится.
Четыре способа сделать одно и то же
Модуль про строки дал четыре набора средств, и почти любую задачу можно решить несколькими из них. Новой темы сегодня не будет — будет выбор.
| инструмент | занятие | когда он лучший |
|---|---|---|
| перебор символов | 13 | условие сложнее равенства: соседи, позиции, накопление |
| методы | 14 | готовая операция: посчитать, найти, заменить, разбить |
| срезы | 15 | нужен кусок: край, середина, разворот, сдвиг |
коды ord и chr |
16 | буква участвует в арифметике: сдвиги, номера, регистр |
Задача «сколько раз встречается буква» решается методом count — и это правильный ответ. Задача «сколько раз буква стоит рядом с такой же» методом не решается вовсе: там нужен перебор с номерами.
Практическое правило: сначала посмотрите, нет ли готового метода. Если есть — берите. Если условие требует чего-то, чего в списке методов нет, — возвращайтесь к циклу.
Сегодня темы не подписаны, и рядом стоящие задачи почти никогда не про одно и то же.
Проверка: чем решать
Условие: «дана строка; выведите её без символов, которые совпадают с предыдущим».
Каким средством это делается?
Что ломается в задачах на строки
Ошибки модуля собраны вместе — с признаком, по которому каждая узнаётся.
Границы
s[i + 1]на последнем шаге цикла. Признак:string index out of range.- Срез на единицу короче или длиннее. Признак: ответ похож на правильный, но не тот. Сообщения нет — срез не падает.
s[-k:]приk = 0даёт всю строку, а не пустую.
Неизменяемость
s.replace(...)без присваивания. Признак: программа выводит то, что ввели.s[0] = "x"— так нельзя, строка собирается заново.
Пустое и вырожденное
- Строка из одного символа: нет соседей, первый и последний совпадают.
split()на строке из пробелов даёт пустой список — цикл не выполнится ни разу.- Искомого нет:
findвернёт −1, и это не позиция.
Коды
- Забытый
% 26— сдвиг уезжает заzв посторонние знаки. - Перепутанные
ord("a")иord("A")для разных регистров.
И ещё про скорость
В задачах на строки есть отдельная ловушка: решение верное, а времени не хватает.
Чаще всего виноват поиск внутри перебора. Например, «сколько различных символов в строке» через проверку каждого символа против всех предыдущих — это действий. На строке в сто тысяч символов получается десять миллиардов — не пройдёт.
Что делать, зависит от задачи, но два приёма закрывают почти всё:
- перебирать алфавит, а не строку. Букв всего двадцать шесть. Вместо «для каждого символа проверить остальные» — «для каждой из 26 букв посчитать, сколько раз она встречается». Это вместо ;
- помнить предыдущее. Задачи про соседей, цепочки и повторы решаются одним проходом, если хранить один символ назад.
Прикидка та же, что в занятии 11: перемножьте длину строки на количество проходов по ней и сравните с десятью миллионами.
Практикум: пятнадцать задач
Лестница прежняя: пять разминочных, семь основных, три со звёздочкой. Зачёт при десяти решённых.
Подсказки в условиях остались, но они про выбор инструмента, а не про решение.
После задач — три разбора чужого кода. Все три программы выглядят правдоподобно, и все три неверны по-разному: одна ошибается на границе, вторая работает верно, но слишком долго, третья ломается на вырожденном вводе. Различать эти три случая по симптому — навык не менее полезный, чем умение писать самому.
Разбор первый: ошибка на границе
Задача: «дана строка; выведите её последние три символа. Если символов меньше трёх, выведите всю строку».
Ученик написал:
s = input()
print(s[len(s) - 3:])
На строке abcdef программа отвечает верно — def. На строке ab она выводит ab, и это тоже верно. А на строке a выводит a — снова верно.
Ученик считает, что решение правильное. Проверьте это рассуждение: найдите вход, на котором программа ошибается, объясните причину и предложите исправление.
Разбор второй: верно, но долго
Задача: «дана строка из строчных латинских букв длиной до ; выведите, сколько в ней различных символов».
Ученик написал:
s = input()
count = 0
for i in range(len(s)):
seen = False
for j in range(i):
if s[j] == s[i]:
seen = True
if not seen:
count += 1
print(count)
На коротких примерах программа отвечает правильно, но на больших тестах получает превышение времени. Объясните, сколько примерно действий она делает, почему это не проходит, и предложите решение, которое пройдёт.
Разбор третий: вырожденный вход
Задача: «дана строка, состоящая из слов и пробелов; выведите первое слово. Если слов нет, выведите NONE».
Ученик написал:
s = input()
parts = s.split()
if parts[0] == "":
print("NONE")
else:
print(parts[0])
На обычных строках программа работает. На строке из одних пробелов она завершается с ошибкой IndexError: list index out of range. Объясните, что произошло, и как проверить отсутствие слов правильно.