Палиндром – текст, который читается одинаково слева направо и справа налево.
Со словом 'шалаш' всё просто. С «А роза упала на лапу Азора» – нет: там пробелы и две заглавные буквы. Для человека это не помеха, а для программы 'А' == 'а' – ложь: это два разных символа с разными кодовыми точками (уроки 8.1 и 8.2).
Поэтому решение делится на два независимых шага, и путать их не надо:
- нормализация – оставить только буквы и привести их к одному регистру;
- проверка – сравнить получившееся с самим собой наоборот.
Первый шаг – ровно тот цикл, что собирал очищенную строку в уроке 8.2: идём по символам, спрашиваем у каждого isalpha(), подходящие приклеиваем в нижнем регистре к новой строке. Второй шаг после этого – одно сравнение:
clean == clean[::-1]
Срез [::-1] разворачивает строку целиком, а палиндром от не-палиндрома отличает ровно то, что перевёрнутая строка равна исходной. Длина при развороте не меняется, так что сравнение всегда честное. Края разбираются сами собой: пустая строка и строка из одного символа – палиндромы, потому что переворачивать в них нечего.
И честная оговорка. В уроке 8.2 сказано, что собирать строку в цикле склейкой не надо и правильный инструмент – join(). Это по-прежнему правда, но чтобы отдать join() куски, нужен список и метод append(), а списками мы займёмся в блоке 9. Так что сейчас собираем склейкой – сознательно и понимая, что это не окончательный вариант.
Второй способ – два указателя. Разумный вопрос: если одной строки хватает, зачем что-то ещё? Затем, что срез [::-1] строит новую строку целиком – копию всей исходной, до последнего символа, – и только потом сравнивает. Человек, проверяя палиндром глазами, так не делает: он берёт первую и последнюю буквы, сравнивает и останавливается на первом же несовпадении. У слова 'арбуз' первая буква «а», последняя «з» – ответ известен после одного сравнения, остальные буквы можно не смотреть.
Приём называется два указателя: переменная i идёт слева направо, j – справа налево, работа продолжается, пока i < j. Как только s[i] != s[j], ответ False и break; если указатели встретились – все пары совпали, ответ True. Сравнений выходит вдвое меньше, чем символов в строке, а на нечётной длине центральный символ не проверяется вообще – сравнивать его не с чем.
Ответ у обоих способов один и тот же, различаются они ценой: один строит копию всей строки, второй обходится двумя переменными. Что писать в реальной программе – обычно первый, он короче и читается мгновенно; второй важен как приём, и «двумя концами навстречу» мы ещё вернёмся, когда дойдём до списков и сортировок.