{ }codelper
key existscounting with a mapsumP0179

A small library's loan ledger

A small library has exactly one copy of each book, and writes every loan and return in a ledger, one line each. The first line gives the number of records q, and each of the next q lines gives a record "out day book" or "in day book". A day is an integer counted from 0 on the day the library opened, and the records are given in an order where the day never decreases. "out d b" records lending book b on day d. If the book is already on loan, print one line "busy b" and treat this record as if it never happened. Otherwise book b is on loan from day d, and book b's loan count goes up by 1. "in d b" records getting book b back on day d. If the book is not on loan, print one line "unknown b" and treat this record as if it never happened. Otherwise, if it was lent on day s, it was borrowed for d − s days, and book b is back in the library. A loan of 14 days or fewer has no late fee; beyond 14 days the fee is 100 won for each extra day. After handling all records, print one line "fee totalFee". totalFee is the sum of the late fees of all returned loans; books never returned are not included. On the last line print the book with the most loans as "top book count". If several books share that count, print the one whose name comes first in dictionary order. If there were no loans at all, print "top none" on the last line. Dictionary order compares names letter by letter from the start, and the earlier letter comes first; if one name is the beginning of the other, the shorter one comes first.

Input
1 ≤ q ≤ 10,000, 0 ≤ day ≤ 1,000,000 (integers, never decreasing).
Book names are 1 to 10 lowercase English letters, with at most 1,000 different books.
The first word is out or in
Output
The busy and unknown lines in record order, then "fee totalFee" and "top book count" (or "top none")
Example 1 · stdin
8
out 1 moby
out 3 dune
out 5 moby
in 15 moby
in 20 dune
out 20 dune
in 40 emma
in 50 dune
stdout
busy moby
unknown emma
fee 1900
top dune 2
Example 2 · stdin
4
out 0 atlas
out 0 atlas
in 16 atlas
in 16 atlas
stdout
busy atlas
unknown atlas
fee 200
top atlas 1
main.py
Input (stdin)

When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).

$ Ready

Open this on a computer to write and run code. On mobile you can read the problem and the solutions.

In other languagesShow solutions by language · 8 languages

This uses two maps.
A map from book on loan → day lent answers whether a key exists, and a map from book → loan count does the counting.
Key exists: Python book in on_loan, JS/TS onLoan.has(book), Java containsKey, C# ContainsKey (there is also Remove(book, out int start), which removes and returns the value), C++ find(book) != end(), Craykon onLoan.Contains(book) (Korean 있나).
C has no map, so it searches a table of books in order and uses −1 for "not on loan", because day 0 is a real day.
Trap one: a book lent on day 0 has the value 0, so asking the truth of the value, as in Python if on_loan.get(book): or JS if (onLoan.get(book)), makes it look not on loan.
Ask whether the key exists.
C++ if (onLoan[book]) also asks about the value, so it is the same trap, and on top of that it inserts the missing book with day 0.
Counting: Python loans.get(book, 0) + 1, JS/TS (loans.get(book) ?? 0) + 1, Java merge(book, 1, Integer::sum), C# GetValueOrDefault(book, 0) + 1, C++ loans[book]++, and in Craykon put a 0 first with Contains and then add.
Trap two: Python Counter.most_common(1) gives the book counted first among equal counts, which is not dictionary order.
Compare the name too, as in min(loans, key=lambda b: (-loans[b], b)), or walk the keys in order as with Java TreeMap or C++ std::map and replace only when strictly larger.
Trap three: the fee total reaches 1,000 books × about 100 million won = about 100 billion won, which overflows Java/C# int and C int.
Use long or long long; Python, JS, TS and Craykon stay below 2^53 and are fine.
14 days is not late, so the fee is max(0, days − 14) × 100.

Pythonmain.py · 31 lines
import sys lines = sys.stdin.read().split("\n")q = int(lines[0])on_loan = {}      # 책 → 빌려 간 날loans = {}        # 책 → 빌려 간 횟수fee = 0out = []for line in lines[1:1 + q]:    kind, day, book = line.split()    day = int(day)    if kind == "out":        if book in on_loan:             # 빌려 간 날이 0일 수도 있으니 값이 아니라 열쇠가 있는지 묻는다            out.append(f"busy {book}")            continue        on_loan[book] = day        loans[book] = loans.get(book, 0) + 1    else:        if book not in on_loan:            out.append(f"unknown {book}")            continue        kept = day - on_loan.pop(book)        fee += max(0, kept - 14) * 100 out.append(f"fee {fee}")if loans:    best = min(loans, key=lambda b: (-loans[b], b))   # 많이 빌린 책, 같으면 사전 순으로 앞선 책    out.append(f"top {best} {loans[best]}")else:    out.append("top none")print("\n".join(out))
JavaScriptmain.js · 35 lines
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const onLoan = new Map(); // 책 → 빌려 간 날const loans = new Map(); // 책 → 빌려 간 횟수let fee = 0;const out = [];for (const line of lines.slice(1, 1 + q)) {  const [kind, d, book] = line.trim().split(" ");  const day = Number(d);  if (kind === "out") {    // 빌려 간 날이 0이면 get()이 0이라 거짓이 된다: has()로 열쇠가 있는지 묻는다    if (onLoan.has(book)) {      out.push(`busy ${book}`);      continue;    }    onLoan.set(book, day);    loans.set(book, (loans.get(book) ?? 0) + 1);  } else {    if (!onLoan.has(book)) {      out.push(`unknown ${book}`);      continue;    }    const kept = day - onLoan.get(book);    onLoan.delete(book);    fee += Math.max(0, kept - 14) * 100;  }} out.push(`fee ${fee}`);let best = null;for (const [book, n] of loans) {  if (best === null || n > loans.get(best) || (n === loans.get(best) && book < best)) best = book;}out.push(best === null ? "top none" : `top ${best} ${loans.get(best)}`);console.log(out.join("\n"));
TypeScriptmain.ts · 40 lines
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const onLoan = new Map<string, number>(); // 책 → 빌려 간 날const loans = new Map<string, number>(); // 책 → 빌려 간 횟수let fee = 0;const out: string[] = [];for (const line of lines.slice(1, 1 + q)) {  const [kind, d, book] = line.trim().split(" ");  const day = Number(d);  if (kind === "out") {    // 빌려 간 날이 0이면 get()이 0이라 거짓이 된다: has()로 열쇠가 있는지 묻는다    if (onLoan.has(book)) {      out.push(`busy ${book}`);      continue;    }    onLoan.set(book, day);    loans.set(book, (loans.get(book) ?? 0) + 1);  } else {    const start = onLoan.get(book);    if (start === undefined) {      out.push(`unknown ${book}`);      continue;    }    onLoan.delete(book);    fee += Math.max(0, day - start - 14) * 100;  }} out.push(`fee ${fee}`);let best: string | null = null;let most = 0;for (const [book, n] of loans) {  if (best === null || n > most || (n === most && book < best)) {    best = book;    most = n;  }}out.push(best === null ? "top none" : `top ${best} ${most}`);console.log(out.join("\n"));
JavaMain.java · 44 lines
import java.io.*;import java.util.*; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int q = Integer.parseInt(br.readLine().trim());        Map<String, Integer> onLoan = new HashMap<>(); // 책 → 빌려 간 날        Map<String, Integer> loans = new TreeMap<>();  // 책 → 빌려 간 횟수(사전 순으로 돈다)        long fee = 0; // 1,000권 × 약 1억 원이라 int로는 넘친다        StringBuilder out = new StringBuilder();        for (int i = 0; i < q; i++) {            String[] p = br.readLine().trim().split(" ");            int day = Integer.parseInt(p[1]);            String book = p[2];            if (p[0].equals("out")) {                if (onLoan.containsKey(book)) {                    out.append("busy ").append(book).append('\n');                    continue;                }                onLoan.put(book, day);                loans.merge(book, 1, Integer::sum);            } else {                Integer start = onLoan.remove(book); // 없으면 null                if (start == null) {                    out.append("unknown ").append(book).append('\n');                    continue;                }                fee += Math.max(0, day - start - 14) * 100L;            }        }        out.append("fee ").append(fee).append('\n');        String best = null;        int most = 0;        for (Map.Entry<String, Integer> e : loans.entrySet()) {            if (e.getValue() > most) { // 사전 순으로 도니 같으면 앞의 것이 남는다                best = e.getKey();                most = e.getValue();            }        }        out.append(best == null ? "top none" : "top " + best + " " + most).append('\n');        System.out.print(out);    }}
C#Main.cs · 53 lines
using System;using System.Collections.Generic;using System.Text; class Program{    static void Main()    {        int q = int.Parse(Console.ReadLine().Trim());        var onLoan = new Dictionary<string, int>(); // 책 → 빌려 간 날        var loans = new Dictionary<string, int>();  // 책 → 빌려 간 횟수        long fee = 0; // 1,000권 × 약 1억 원이라 int로는 넘친다        var sb = new StringBuilder();        for (int i = 0; i < q; i++)        {            string[] p = Console.ReadLine().Trim().Split(' ');            int day = int.Parse(p[1]);            string book = p[2];            if (p[0] == "out")            {                if (onLoan.ContainsKey(book))                {                    sb.Append($"busy {book}\n");                    continue;                }                onLoan[book] = day;                loans[book] = loans.GetValueOrDefault(book, 0) + 1;            }            else            {                if (!onLoan.Remove(book, out int start)) // 있으면 빼면서 값을 꺼낸다                {                    sb.Append($"unknown {book}\n");                    continue;                }                fee += Math.Max(0, day - start - 14) * 100L;            }        }        sb.Append($"fee {fee}\n");        string best = null;        int most = 0;        foreach (var (book, n) in loans)        {            if (best == null || n > most || (n == most && string.CompareOrdinal(book, best) < 0))            {                best = book;                most = n;            }        }        sb.Append(best == null ? "top none\n" : $"top {best} {most}\n");        Console.Write(sb);    }}
C++main.cpp · 36 lines
#include <algorithm>#include <iostream>#include <map>#include <string> int main() {    std::ios::sync_with_stdio(false);    int q;    std::cin >> q;    std::map<std::string, int> onLoan; // 책 → 빌려 간 날    std::map<std::string, int> loans;  // 책 → 빌려 간 횟수(열쇠 순으로 돈다)    long long fee = 0; // 1,000권 × 약 1억 원이라 int로는 넘친다    std::string out;    for (int i = 0; i < q; i++) {        std::string kind, book;        int day;        std::cin >> kind >> day >> book;        auto it = onLoan.find(book); // onLoan[book]으로 물으면 없는 책이 0일로 생겨 버린다        if (kind == "out") {            if (it != onLoan.end()) { out += "busy " + book + "\n"; continue; }            onLoan[book] = day;            loans[book]++;        } else {            if (it == onLoan.end()) { out += "unknown " + book + "\n"; continue; }            fee += std::max(0, day - it->second - 14) * 100LL;            onLoan.erase(it);        }    }    out += "fee " + std::to_string(fee) + "\n";    std::string best;    int most = 0;    for (const auto& [book, n] : loans)        if (n > most) { best = book; most = n; } // 사전 순으로 도니 같으면 앞의 것이 남는다    out += most == 0 ? "top none\n" : "top " + best + " " + std::to_string(most) + "\n";    std::cout << out;}
Cmain.c · 49 lines
#include <stdio.h>#include <string.h> /* C에는 사전이 없다: 책은 많아야 1,000권이라 책 표를 차례로 찾는다 */static char title[1000][11];static int out_day[1000];   /* 빌려 간 날, 빌려 가지 않았으면 -1 */static int loans[1000];     /* 빌려 간 횟수 */static int count = 0; static int find_or_add(const char* book) {    for (int i = 0; i < count; i++)        if (strcmp(title[i], book) == 0) return i;    strcpy(title[count], book);    out_day[count] = -1; /* 0일에 빌려 갈 수 있으니 "없음"은 -1로 둔다 */    loans[count] = 0;    return count++;} int main(void) {    int q;    scanf("%d", &q);    long long fee = 0; /* 1,000권 × 약 1억 원이라 int로는 넘친다 */    for (int e = 0; e < q; e++) {        char kind[4], book[11];        int day;        scanf("%3s %d %10s", kind, &day, book);        int b = find_or_add(book);        if (strcmp(kind, "out") == 0) {            if (out_day[b] >= 0) { printf("busy %s\n", book); continue; }            out_day[b] = day;            loans[b]++;        } else {            if (out_day[b] < 0) { printf("unknown %s\n", book); continue; }            int late = day - out_day[b] - 14;            if (late > 0) fee += late * 100LL;            out_day[b] = -1;        }    }    printf("fee %lld\n", fee);    int best = -1;    for (int i = 0; i < count; i++) {        if (loans[i] == 0) continue; /* 반납 기록만 있는 책은 빼고 */        if (best < 0 || loans[i] > loans[best] || (loans[i] == loans[best] && strcmp(title[i], title[best]) < 0))            best = i;    }    if (best < 0) printf("top none\n");    else printf("top %s %d\n", title[best], loans[best]);    return 0;}
Craykonmain.cray · 60 lines
q = Console.ReadLine().ToNumber();onLoan = Dict(); // 책 → 빌려 간 날loans = Dict(); // 책 → 빌려 간 횟수fee = 0;for (e = 0; e < q; e++){    p = Console.ReadLine().Trim().Split();    day = p[1].ToNumber();    book = p[2];    if (p[0] == "out")    {        if (onLoan.Contains(book))        {            Console.WriteLine("busy " + book);        }        else        {            onLoan[book] = day;            if (!loans.Contains(book))            {                loans[book] = 0;            }            loans[book] = loans[book] + 1;        }    }    else if (!onLoan.Contains(book))    {        Console.WriteLine("unknown " + book);    }    else    {        late = day - onLoan[book] - 14;        if (late > 0)        {            fee = fee + late * 100;        }        onLoan.Remove(book);    }}Console.WriteLine("fee " + fee); best = null;most = 0;foreach (book in loans.Keys){    n = loans[book];    if (best == null || n > most || n == most && book < best)    {        best = book;        most = n;    }}if (best == null){    Console.WriteLine("top none");}else{    Console.WriteLine("top " + best + " " + most);}