Counters with Closures

Problem

Each input line is inc NAME or get NAME. inc adds 1 to the counter NAME (every name starts at 0) and prints the new value. get prints the current value (0 for an unknown name). Create every counter as a function that keeps its own private count (a closure), not as a global variable.

Constraints

  • 1 <= lines <= 10^4

Examples

Input

inc a
inc a
inc b
get a

Output

1
2
1
2

Solutions

Best approachTime O(n)Space O(n)

A closure is a function together with the variables it uses from the place where it was made. Each call of makeCounter makes a new private count, so the counters do not disturb each other. In Python the keyword nonlocal is needed to change such a variable; in Java a lambda cannot change a local variable at all, so a one-element array is a common trick.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Process lines.
 * Input   : Several lines.
 * Output  : One value per command.
 * Approach: A function that remembers a count.
 * Time    : O(n)
 * Space   : O(n)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
        // Step 1: A counter is a function that increases its own private count. The count lives in a one-element array,
        // because a lambda can read a local variable but cannot change it.
        static java.util.function.IntSupplier makeCounter() {
            int[] count = {0};
            return () -> ++count[0];
        }

    // This method solves the task. It receives the input and returns the answer.
    static String[] solve(String[] lines) {
        // Step 2: One counter function for each name. The functions are made the first time a name is used.
        java.util.Map<String, java.util.function.IntSupplier> counters = new java.util.HashMap<>();
        java.util.Map<String, Integer> last = new java.util.HashMap<>();
        String[] output = new String[lines.length];

        for (int i = 0; i < lines.length; i++) {
            String[] part = lines[i].split(" ");
            if (part[0].equals("inc")) {
                // Step 3: Calling the function increases the private count and returns it.
                counters.computeIfAbsent(part[1], name -> makeCounter());
                int value = counters.get(part[1]).getAsInt();
                last.put(part[1], value);
                output[i] = String.valueOf(value);
            } else {
                // Step 4: get only reads the last value we saw. It does not call the counter function.
                output[i] = String.valueOf(last.getOrDefault(part[1], 0));
            }
        }
        return output;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        java.util.List<String> all = new java.util.ArrayList<>();
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine().trim();
            if (!line.isEmpty()) {
                all.add(line);
            }
        }
        String[] lines = all.toArray(new String[0]);
        System.out.println(String.join("\n", solve(lines)));
    }
}
Hints (1)
  1. Why can the count not be changed from outside?