Что происходит с хвостами списков после основного цикла и как их обработать?
Что проверяют
Аккуратность на границах. Задача звучит просто, но именно на остатке после основного цикла ломается половина решений: одни теряют элементы, другие обрабатывают их дважды и не замечают этого.
Как отвечать
Сначала уточните, какой цикл имеется в виду, вариантов обычно два.
Слияние двух отсортированных списков: основной цикл идёт, пока живы оба указателя, и заканчивается, как только один список исчерпан. Остаток второго уже отсортирован, его дописывают целиком:
while i < len(a) and j < len(b):
...
res.extend(a[i:])
res.extend(b[j:])Срез за границей списка возвращает пустой список, поэтому выяснять, какой из списков закончился, не нужно: обе строки безопасны в любом случае.
Нарезка на батчи устроена иначе, неполный кусок хвостом не является и получается сам:
for i in range(0, len(data), size):
batch = data[i:i + size]Срез обрезается по фактической длине, так что отдельная обработка остатка после цикла означает повторную обработку тех же элементов. Настоящий хвост появляется там, где шаг и окно разного размера, например у скользящего окна, и там решение принимают осознанно: дополнить, отбросить или посчитать по укороченному окну.
Где спотыкаются
Дописывают обработку остатка после цикла по батчам, хотя цикл его уже забрал. На обучающей выборке это тихо задваивает часть данных, и находят ошибку не по падению, а по подозрительно хорошей метрике.
Пишут цикл с условием по любому из указателей и выходят за границу списка. Надёжнее оставить в условии оба указателя, а остатки дописать после цикла срезами, тогда проверок внутри не требуется вовсе.
Обрезают данные молча. Функция zip останавливается на коротком списке, и лишние метки или признаки просто пропадают. Когда длины обязаны совпадать, это проверяют явно или пользуются вариантом со строгой проверкой, а не надеются на совпадение размеров.