К обычному разбору
Тренировка по собеседованию3 задачиТехническое собеседованиеDodo2026-04-28

Dodo: Техническое собеседование

Идите сверху вниз: сначала попробуйте сами, затем откройте разбор. Если шаг с кодом, пишите решение прямо здесь и запускайте проверки на странице.

1SQL-задачаMedium

Средняя цена конкурентов в окне плюс-минус 3 дня

Условие

Есть таблица цен конкурентов. Для каждой пары product_id и price_date нужно посчитать среднюю цену этого же продукта по всем конкурентам в окне price_date - 3 дня ... price_date + 3 дня.

Верните:

  • product_id
  • price_date
  • avg_price_7d — средняя цена, округленная до 2 знаков

Результат отсортируйте по product_id, затем price_date.

Решение прямо на странице

Напишите код, запустите проверки и только потом открывайте разбор.

Проверка решения

Нажмите «Запустить проверки» или Ctrl+Enter.

Показать разбор

Подсказки

  • Нужен self join

    Одна копия таблицы задает базовую дату, вторая дает строки из окна вокруг нее.

  • Окно задается датами

    В SQLite используйте DATE(price_date, '-3 day') и DATE(price_date, '+3 day').

Идея решения

Сначала берем distinct список дат, по которым нужен ответ. Затем self-join по тому же product_id и условию на дату в пределах трех дней от базовой даты.

После этого группируем по product_id, price_date, считаем AVG(price) и округляем результат.

Эталонный код

SELECT
  base.product_id,
  base.price_date,
  ROUND(AVG(window_prices.price), 2) AS avg_price_7d
FROM (
  SELECT DISTINCT product_id, price_date
  FROM competitor_prices
) AS base
JOIN competitor_prices AS window_prices
  ON window_prices.product_id = base.product_id
 AND window_prices.price_date BETWEEN DATE(base.price_date, '-3 day') AND DATE(base.price_date, '+3 day')
GROUP BY base.product_id, base.price_date
ORDER BY base.product_id, base.price_date;
Сложность
Время: O(n^2) без индексов. Память: O(g).
Self join сопоставляет строки одной таблицы с другой; на практике нужен индекс по product_id, price_date.
2SQL-задачаMedium

Последние чеки клиентов с большим весом пиццы

Условие

Есть таблицы чеков, позиций чека и справочник SKU.

Анализируем апрель 2026: check_date >= '2026-04-01' и check_date < '2026-05-01'.

Нужно найти клиентов, у которых суммарный вес купленной пиццы за апрель строго больше 3.0 kg. Для таких клиентов верните последние 2 чека за тот же месяц.

Важно: последние чеки возвращаются независимо от состава чека. Если клиент прошел порог по пицце, в ответ могут попасть и его непицца-чеки.

Верните:

  • client_id;
  • check_id;
  • check_date.

Сортировка: client_id ASC, check_date DESC, check_id DESC.

Schema

CREATE TABLE checks (
  check_id INTEGER PRIMARY KEY,
  client_id INTEGER NOT NULL,
  check_date TEXT NOT NULL
);

CREATE TABLE check_items (
  check_id INTEGER NOT NULL,
  sku_id INTEGER NOT NULL,
  quantity INTEGER NOT NULL
);

CREATE TABLE sku (
  sku_id INTEGER PRIMARY KEY,
  category TEXT NOT NULL,
  unit_weight_kg REAL NOT NULL
);

Решение прямо на странице

Напишите код, запустите проверки и только потом открывайте разбор.

Проверка решения

Нажмите «Запустить проверки» или Ctrl+Enter.

Показать разбор

Подсказки

  • Порог считается по pizza items

    Вес для отбора клиента считайте только по SKU категории pizza.

  • Последние чеки — все чеки

    После отбора клиента ранжируйте все его чеки месяца, не только pizza-чеки.

Идея решения

Сначала ограничиваем чеки апрелем 2026. Затем через join с позициями и SKU считаем суммарный вес только строк категории pizza и оставляем клиентов с весом > 3.0.

После этого ранжируем все апрельские чеки таких клиентов через ROW_NUMBER() по дате и id чека в обратном порядке. В финальном ответе берем rn <= 2.

Эталонный код

WITH month_checks AS (
  SELECT check_id, client_id, check_date
  FROM checks
  WHERE check_date >= '2026-04-01'
    AND check_date < '2026-05-01'
),
pizza_weights AS (
  SELECT
    mc.client_id,
    SUM(ci.quantity * s.unit_weight_kg) AS pizza_weight_kg
  FROM month_checks mc
  JOIN check_items ci ON ci.check_id = mc.check_id
  JOIN sku s ON s.sku_id = ci.sku_id
  WHERE LOWER(s.category) = 'pizza'
  GROUP BY mc.client_id
  HAVING SUM(ci.quantity * s.unit_weight_kg) > 3.0
),
ranked_checks AS (
  SELECT
    mc.client_id,
    mc.check_id,
    mc.check_date,
    ROW_NUMBER() OVER (
      PARTITION BY mc.client_id
      ORDER BY mc.check_date DESC, mc.check_id DESC
    ) AS rn
  FROM month_checks mc
  JOIN pizza_weights pw ON pw.client_id = mc.client_id
)
SELECT client_id, check_id, check_date
FROM ranked_checks
WHERE rn <= 2
ORDER BY client_id ASC, check_date DESC, check_id DESC;
Сложность
Время: O(n log n). Память: O(n).
Агрегация считает вес по клиентам, затем ROW_NUMBER сортирует чеки внутри каждого клиента.
3ЗадачаEasy

Pizza tag ranker

Условие

Нужно ранжировать пиццы по тегам.

Даны:

  • tag_weights: вес каждого тега;
  • pizzas: список пицц в формате {"id": string, "tags": string[]};
  • limit: сколько лучших пицц вернуть.

Score пиццы — сумма весов всех ее тегов, которых нет в tag_weights, дают 0. Верните id пицц по убыванию score. Если score одинаковый, сортируйте по id лексикографически.

Сигнатура

def rank_pizzas(tag_weights: dict[str, float], pizzas: list[dict], limit: int) -> list[str]:

Решение прямо на странице

Напишите код, запустите проверки и только потом открывайте разбор.

Проверка решения

Нажмите «Запустить проверки» или Ctrl+Enter.

Показать разбор

Подсказки

  • Вес неизвестного тега

    Если тега нет в словаре весов, его вклад равен 0.

  • Стабильный tie-breaker

    Сортируйте не только по score, но и по id.

Идея решения

Для каждой пиццы суммируем веса тегов через hash map tag_weights. Затем сортируем пары (-score, id) и берем первые limit элементов.

Такой tie-breaker делает результат стабильным и не зависит от исходного порядка списка.

Эталонный код

def rank_pizzas(tag_weights: dict[str, float], pizzas: list[dict], limit: int) -> list[str]:
    scored = []
    for pizza in pizzas:
        score = sum(tag_weights.get(tag, 0) for tag in pizza.get('tags', []))
        scored.append((pizza['id'], score))

    scored.sort(key=lambda item: (-item[1], item[0]))
    return [pizza_id for pizza_id, _score in scored[:limit]]
Сложность
Время: O(n * t + n log n). Память: O(n).
Считаем score по тегам каждой пиццы, затем сортируем все n пицц.