From d47ca83d2e429cd1a753f51247bb0837e383c1cb Mon Sep 17 00:00:00 2001 From: Andrei Ovcharenko Date: Fri, 31 Jul 2026 17:44:44 +0300 Subject: [PATCH] Harden sprint4 CLI input handling --- .../java/algorithms/sprint4/FindSystem.java | 44 +++++++++---- src/main/java/algorithms/sprint4/Map.java | 61 ++++++++++++++----- 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/src/main/java/algorithms/sprint4/FindSystem.java b/src/main/java/algorithms/sprint4/FindSystem.java index 4d20189..90f31dc 100644 --- a/src/main/java/algorithms/sprint4/FindSystem.java +++ b/src/main/java/algorithms/sprint4/FindSystem.java @@ -9,11 +9,17 @@ import java.util.HashSet; import java.util.Map; import java.util.StringTokenizer; +import java.io.BufferedWriter; +import java.io.OutputStreamWriter; // https://contest.yandex.ru/contest/24414/run-report/160043341/ class FindSystem { + private static final int MAX_DOCUMENTS = 10_000; + private static final int MAX_QUERIES = 10_000; + private static final int MAX_LINE_LENGTH = 10_000; + /* * Принцип работы алгоритма: * 1) Строим обратный индекс: @@ -141,23 +147,24 @@ private static boolean isBetter(int docId1, int score1, int docId2, int score2) private static void solve() throws Exception { FastReader reader = new FastReader(System.in); - int n = reader.nextInt(); + int n = reader.nextInt(MAX_DOCUMENTS); String[] docs = new String[n]; for (int i = 0; i < n; i++) { - docs[i] = reader.nextLine(); + docs[i] = reader.nextLine(MAX_LINE_LENGTH); } HashMap> index = buildIndex(docs); - int m = reader.nextInt(); - StringBuilder out = new StringBuilder(); + int m = reader.nextInt(MAX_QUERIES); + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); for (int i = 0; i < m; i++) { - String query = reader.nextLine(); - out.append(processQuery(query, index)).append('\n'); + String query = reader.nextLine(MAX_LINE_LENGTH); + out.write(processQuery(query, index)); + out.newLine(); } - System.out.print(out); + out.flush(); } private static void test() { @@ -205,7 +212,11 @@ public static void main(String[] args) throws Exception { if (System.getProperty("os.name").startsWith("Windows")) { test(); } else { - solve(); + try { + solve(); + } catch (IOException ignored) { + // Invalid or excessive input is rejected without exhausting memory or CPU. + } } } private static class FastReader { @@ -229,7 +240,7 @@ private int read() throws IOException { return buffer[ptr++]; } - int nextInt() throws IOException { + int nextInt(int max) throws IOException { int c; do { c = read(); @@ -238,15 +249,21 @@ int nextInt() throws IOException { } } while (c <= ' '); - int value = 0; + long value = 0; while (c > ' ') { + if (c < '0' || c > '9') { + throw new IOException("Expected a non-negative integer"); + } value = value * 10 + c - '0'; + if (value > max) { + throw new IOException("Input value exceeds limit"); + } c = read(); } - return value; + return (int) value; } - String nextLine() throws IOException { + String nextLine(int maxLength) throws IOException { int c = read(); while (c == '\n' || c == '\r') { @@ -255,6 +272,9 @@ String nextLine() throws IOException { StringBuilder sb = new StringBuilder(); while (c != -1 && c != '\n' && c != '\r') { + if (sb.length() == maxLength) { + throw new IOException("Input line exceeds limit"); + } sb.append((char) c); c = read(); } diff --git a/src/main/java/algorithms/sprint4/Map.java b/src/main/java/algorithms/sprint4/Map.java index 4131c92..793d148 100644 --- a/src/main/java/algorithms/sprint4/Map.java +++ b/src/main/java/algorithms/sprint4/Map.java @@ -1,9 +1,12 @@ package algorithms.sprint4; import java.io.BufferedInputStream; +import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStreamWriter; import java.util.OptionalInt; +import java.util.concurrent.ThreadLocalRandom; // https://contest.yandex.ru/contest/24414/run-report/160371601/ @@ -51,6 +54,8 @@ public class Map { */ private static final int SIZE = 100_003; + private static final int MAX_COMMANDS = 100_000; + private static final int HASH_SEED = ThreadLocalRandom.current().nextInt(); static class Node { int key; @@ -68,11 +73,9 @@ static class HashTable { Node[] buckets = new Node[SIZE]; int index(int key) { - int x = key % SIZE; - if (x < 0) { - x += SIZE; - } - return x; + int mixed = key ^ HASH_SEED; + mixed ^= (mixed >>> 16); + return Math.floorMod(mixed, SIZE); } void put(int key, int value) { @@ -144,8 +147,15 @@ int read() throws IOException { } int nextInt() throws IOException { + return nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + int nextInt(int min, int max) throws IOException { int c = read(); while (c <= ' ') { + if (c == -1) { + throw new IOException("Unexpected end of input"); + } c = read(); } @@ -155,13 +165,24 @@ int nextInt() throws IOException { c = read(); } - int num = 0; + if (c < '0' || c > '9') { + throw new IOException("Expected integer"); + } + + long num = 0; while (c > ' ') { + if (c < '0' || c > '9') { + throw new IOException("Expected integer"); + } num = num * 10 + c - '0'; + long signed = sign * num; + if (signed < min || signed > max) { + throw new IOException("Input value exceeds limit"); + } c = read(); } - return num * sign; + return (int) (num * sign); } char nextCommand() throws IOException { @@ -181,11 +202,19 @@ char nextCommand() throws IOException { } public static void main(String[] args) throws Exception { + try { + solve(); + } catch (IOException ignored) { + // Invalid or excessive input is rejected without exhausting memory or CPU. + } + } + + private static void solve() throws IOException { Reader reader = new Reader(); HashTable table = new HashTable(); - StringBuilder out = new StringBuilder(); + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); - int n = reader.nextInt(); + int n = reader.nextInt(0, MAX_COMMANDS); for (int i = 0; i < n; i++) { char command = reader.nextCommand(); @@ -195,14 +224,18 @@ public static void main(String[] args) throws Exception { int value = reader.nextInt(); table.put(key, value); } else if (command == 'g') { - table.get(key).ifPresentOrElse(out::append, () -> out.append("None")); - out.append(System.lineSeparator()); + OptionalInt result = table.get(key); + out.write(result.isPresent() ? String.valueOf(result.getAsInt()) : "None"); + out.newLine(); + } else if (command == 'd') { + OptionalInt result = table.delete(key); + out.write(result.isPresent() ? String.valueOf(result.getAsInt()) : "None"); + out.newLine(); } else { - table.delete(key).ifPresentOrElse(out::append, () -> out.append("None")); - out.append(System.lineSeparator()); + throw new IOException("Unknown command"); } } - System.out.print(out); + out.flush(); } }