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
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.
/*
* 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)
- Why can the count not be changed from outside?