Excel-like ячейки с формулами sum и проверкой циклов
Реализуйте упрощенный Excel.
Поддерживаются операции:
set(cell, value): записать число в ячейку и удалить старую формулу этой ячейки;get(cell): вернуть текущее значение ячейки, пустая ячейка равна0;sum(cell, refs): записать в ячейку формулу суммы других ячеек и вернуть текущее значение.
Если новая формула создает цикл зависимостей, состояние менять нельзя. Wrapper excel_results должен вернуть строку "cycle" для такой операции.
Сигнатура wrapper
def excel_results(operations: list[list]) -> list:Решение прямо на странице
Напишите код, запустите проверки и только потом открывайте разбор.
Нажмите «Запустить проверки» или Ctrl+Enter.
Показать разбор
Подсказки
- Формула хранит зависимости
sum(C1, [A1, B1]) означает C1 -> A1 и C1 -> B1.
- Цикл проверяется до записи
Если зависимость плохая, старое состояние должно остаться прежним.
Идея решения
Храним два словаря: простые значения и формулы cell -> refs. get рекурсивно вычисляет формулы.
Перед записью новой формулы проверяем, станет ли cell достижима из любого ref. Если да, новая формула создает цикл, ее нужно отклонить и оставить старое состояние.
Эталонный код
class Excel:
def __init__(self):
self.values: dict[str, int] = {}
self.formulas: dict[str, list[str]] = {}
def set(self, cell: str, value: int) -> None:
self.values[cell] = value
self.formulas.pop(cell, None)
def get(self, cell: str) -> int:
return self._get(cell, set())
def _get(self, cell: str, visiting: set[str]) -> int:
if cell in visiting:
raise ValueError('cycle')
if cell not in self.formulas:
return self.values.get(cell, 0)
visiting.add(cell)
value = sum(self._get(ref, visiting) for ref in self.formulas[cell])
visiting.remove(cell)
return value
def _depends_on(self, start: str, target: str, seen: set[str]) -> bool:
if start == target:
return True
if start in seen:
return False
seen.add(start)
return any(self._depends_on(ref, target, seen) for ref in self.formulas.get(start, []))
def sum(self, cell: str, refs: list[str]) -> int:
if any(self._depends_on(ref, cell, set()) for ref in refs):
raise ValueError('cycle')
self.formulas[cell] = list(refs)
self.values.pop(cell, None)
return self.get(cell)
def excel_results(operations: list[list]) -> list:
excel = Excel()
output = []
for op in operations:
command = op[0]
if command == 'set':
excel.set(op[1], op[2])
elif command == 'get':
output.append(excel.get(op[1]))
elif command == 'sum':
try:
output.append(excel.sum(op[1], op[2]))
except ValueError:
output.append('cycle')
return outputUndo/redo для движка формул как в Excel
Undo/redo для движка формул как в Excel
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Store immutable cell states in per-cell history with a current index. Undo moves the index back and restores that state; redo moves forward. Any new set/sum after undo must truncate future states before appending the new state.
Подробный разбор
Represent a cell state explicitly: either a constant value or a formula reference list. For each cell, keep a history array of states and a current position. The active state of a cell is history[cell][position[cell]], with a missing state meaning an empty zero cell.
On set or sum, first validate the new operation. For sum, cycle detection must happen before mutating durable state. Then, if the cell is not at the end of its history, truncate all states after the current position. Append the new state and advance the position.
Undo(cell) checks whether position[cell] can move left. If so, decrement it and restore the previous state. Redo(cell) checks whether it can move right and increments it. Restoration should be centralized in one helper so constants, formulas and empty cells are handled consistently.
The main edge cases are formulas depending on cells whose own state later changes, cycles introduced by restored formulas, and whether undo/redo is per-cell or global-command based. In the interview prompt, per-cell history is enough, but in a product you should define these semantics up front.
Типичные ошибки
- Pop history on undo, which makes redo impossible.
- Forget to truncate redo history after a new operation.
- Store only numeric values and lose formula dependencies.
- Mutate state before validating a formula cycle.
Как сказать на собеседовании
- Define the state object before writing stack logic.
- Say whether history is per-cell or global-command based.