Learning pathOriginal reference
Lesson 11 of 12

Cancellation, undo, and combined tools

Use a reduced-prefix invariant, preserve action history, and combine a queue with a stack.

A cancellation can reveal the next one

To remove adjacent equal character pairs, keep the reduced prefix on a stack. If the next character equals the top, pop instead of pushing it. Otherwise push it. For abbaca, the states are a, ab, a, empty, c, ca.

The stack always holds a prefix with no adjacent equal pair. Only the boundary with the new character can need cancellation. This avoids restarting a full scan after each deletion. Every character is pushed at most once and popped at most once, so total processing is linear.

C++23
// Complete C++23 program. Input: one lowercase ASCII line.
#include <algorithm>
#include <iostream>
#include <stack>
#include <string>
int main() {
    std::string text;
    std::getline(std::cin, text);
    std::stack<char> kept;
    for (char c : text) {
        if (!kept.empty() && kept.top() == c) kept.pop();
        else kept.push(c);
    }
    std::string answer;
    while (!kept.empty()) {
        answer.push_back(kept.top());
        kept.pop();
    }
    std::reverse(answer.begin(), answer.end());
    std::cout << (answer.empty() ? "EMPTY" : answer) << '\n';
}

Undo reverses the latest uncancelled action

For additions and undo, keep the current total plus a stack of uncancelled amounts. Add pushes the amount and increases the total. Undo copies the top amount, subtracts it, and removes that action. An undo on empty history can do nothing when the contract says so.

Negative additions are still actions. Undoing an addition of −3 increases the total by three. A wide running total avoids overflow from many individually small amounts. Maintaining it is constant work per command; rescanning the entire history after each command can be quadratic.

Optional: reverse each group without reordering groups

A queue can hold waiting values while a temporary stack reverses one group. Move at most k front values into the stack, then drain it into the output. Continue until the queue is empty, including a shorter final group.

For 1 2 3 4 5 6 7 with k = 3, the result is 3 2 1 6 5 4 7. Require k ≥ 1 so the algorithm progresses. Each value makes a bounded number of queue/stack operations, giving O(n + 1) time, an O(n + 1) queue, and O(min(k, n) + 1) temporary stack.

Container workbench

Insert values, then remove the next accessible item. Priority queue values are shown sorted for explanation, not as an internal heap.

80
31
52

Next removal: 5. Size: 3.

Guided simulation · Change the inputs to test your understanding.

Pause & predict

After adding 10, adding −3, then undoing once, what is the running total?

Choose an answer
Try it yourself

Turn the idea into code

Process commands on total = 0: 1 x adds and remembers x, 2 undoes the most recent uncancelled addition if any, and 3 prints the total. Only type 3 prints. Allow up to 200000 commands with |x| ≤ 10⁹.

Give me a hint

Store each addition in a stack and maintain a long long total. Undo changes the total before popping its amount.

Show the explained solution

The stack holds exactly the actions still contributing to the total. Each action is pushed once and removed at most once, so the whole program is O(q + 1) time with O(q + 1) worst-case storage.

C++23
// Complete C++23 program. Input: q, then commands 1 x, 2, or 3.
#include <iostream>
#include <stack>
int main() {
    int q = 0;
    std::cin >> q;
    std::stack<long long> history;
    long long total = 0;
    while (q--) {
        int type = 0;
        std::cin >> type;
        if (type == 1) {
            long long amount = 0;
            std::cin >> amount;
            history.push(amount);
            total += amount;
        } else if (type == 2) {
            if (!history.empty()) {
                total -= history.top();
                history.pop();
            }
        } else {
            std::cout << total << '\n';
        }
    }
}

Your notes

Ready for the next idea?

Mark complete when you can explain it yourself.

From a problem to a programIdentify the input, output, and constraints, then translate a small plan into C++.Variables, types, and remembered valuesChoose a representation, initialize it, and understand what assignment really changes.Arithmetic without surprisesPredict division and remainder, widen before multiplication, and format decimal answers.Conditions and logical decisionsBuild exclusive categories, combine comparisons, and protect operations with short-circuit logic.Optional: other ways to chooseRecognize switch for exact choices and the conditional operator for choosing a value.For loops: make repetition preciseTrace initialization, condition, body, and update—and avoid an extra iteration.While, do-while, and stopping rulesChoose a loop from its stopping condition and read the exact input format.Break, continue, and early answersDistinguish leaving a loop from skipping one iteration, without losing required input.Five patterns worth understandingUse sums, counts, best-so-far candidates, products, and flags to process a stream.Nested loops: rows and columnsTranslate a shape into two loop roles and count the total work.Take a number apart, one digit at a timeUse remainder and division to process decimal digits, including zero.Multiple cases and a debugging mindsetCombine the patterns, isolate each case, and test the boundaries that can break your idea.Arrays: give every value a placeUnderstand indices, initialization, capacity, active length, and the first safe array loop.Scan, count, search, and compareDistinguish values from positions and preserve the exact tie rule a problem asks for.Change a sequence without losing itUpdate, copy, compare, reverse, and inspect neighbours using the correct bounds.Matrices: think in two coordinatesTraverse tables, total rows and columns, inspect diagonals, and protect grid neighbours.Characters become stringsSeparate characters from numbers, then safely inspect and edit managed text.Read the text the problem actually givesChoose words or full lines, preserve empty cases, and reset only the state that needs it.Scan text: count, filter, transformBuild a result from matching characters without losing leading zeros or skipping shifted text.Join, compare, and find textLearn content comparison, lexicographical order, substring counts, and the not-found sentinel.