Skip to content

Commit 0e23996

Browse files
committed
Address remaining validation review findings
1 parent fefbb9e commit 0e23996

27 files changed

Lines changed: 345 additions & 135 deletions

src/main/java/algorithms/sprint0/Zip.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,16 @@ static void process(BufferedReader reader, BufferedWriter writer) throws IOExcep
6565
private static String readBoundedLine(BufferedReader reader) throws IOException {
6666
StringBuilder line = new StringBuilder();
6767
int character;
68-
while ((character = reader.read()) != -1 && character != '\n') {
68+
while ((character = reader.read()) != -1 && character != '\n' && character != '\r') {
6969
if (line.length() == MAX_INPUT_LINE_LENGTH) {
7070
throw new IllegalArgumentException("Input line is too long");
7171
}
72-
if (character != '\r') {
73-
line.append((char) character);
72+
line.append((char) character);
73+
}
74+
if (character == '\r') {
75+
reader.mark(1);
76+
if (reader.read() != '\n') {
77+
reader.reset();
7478
}
7579
}
7680
return character == -1 && line.length() == 0 ? null : line.toString();

src/main/java/algorithms/sprint1/SleightOfHand.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,10 @@ void flush() throws IOException {
115115
}
116116

117117
public static void main(String[] args) throws Exception {
118-
try {
119-
if (System.getProperty("os.name").startsWith("Windows")) {
120-
test();
121-
} else {
122-
run();
123-
}
124-
} catch (IOException | IllegalArgumentException e) {
125-
System.err.println(e.getMessage());
118+
if (System.getProperty("os.name").startsWith("Windows")) {
119+
test();
120+
} else {
121+
run();
126122
}
127123
}
128124

src/main/java/algorithms/sprint2/Deque.java

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ static final class RingDeque {
6565
private int size = 0;
6666

6767
RingDeque(int cap) {
68-
this.cap = safeCapacity(cap);
68+
this.cap = validateCapacity(cap);
6969
this.a = new int[this.cap];
7070
}
7171

@@ -114,16 +114,19 @@ int popBack() {
114114
}
115115
}
116116

117-
private static int safeCapacity(int cap) {
118-
if (cap < 0) {
119-
return 0;
117+
private static int validateCapacity(int cap) {
118+
if (cap < 0 || cap > MAX_CAPACITY) {
119+
throw new IllegalArgumentException("Deque capacity is out of range");
120120
}
121-
return Math.min(cap, MAX_CAPACITY);
121+
return cap;
122122
}
123123

124124
private static void process(FastIn in, FastOut out) throws Exception {
125125
int n = in.nextInt();
126126
int m = in.nextInt();
127+
if (n < 0 || n > MAX_CAPACITY) {
128+
throw new IllegalArgumentException("Command count is out of range");
129+
}
127130

128131
RingDeque dq = new RingDeque(m);
129132

@@ -244,26 +247,9 @@ private static void test() throws Exception {
244247
)
245248
);
246249

247-
// Некорректная емкость из ввода не должна приводить к аварийному завершению
248-
assertEq(
249-
"error\nerror\n",
250-
solveIO(
251-
"2\n" +
252-
"-1\n" +
253-
"push_back 1\n" +
254-
"pop_front\n"
255-
)
256-
);
257-
258-
// Слишком большая емкость ограничивается безопасным максимумом до выделения массива
259-
assertEq(
260-
"error\n",
261-
solveIO(
262-
"1\n" +
263-
"1000000000\n" +
264-
"pop_front\n"
265-
)
266-
);
250+
// Некорректная емкость отклоняется, а не меняет заявленную семантику дека.
251+
assertRejected("2\n-1\npush_back 1\npop_front\n");
252+
assertRejected("1\n1000000000\npop_front\n");
267253

268254
// Wrap-around: head/tail должны корректно "перепрыгивать" границу массива
269255
assertEq(
@@ -291,6 +277,15 @@ static void assertEq(String exp, String act) {
291277
}
292278
}
293279

280+
private static void assertRejected(String input) throws Exception {
281+
try {
282+
solveIO(input);
283+
throw new AssertionError("Expected invalid deque capacity to be rejected");
284+
} catch (IllegalArgumentException expected) {
285+
// Expected validation failure.
286+
}
287+
}
288+
294289
public static void main(String[] args) throws Exception {
295290
if (System.getProperty("os.name").startsWith("Windows")) {
296291
test();

src/main/java/algorithms/sprint4/FindSystem.java

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.StringTokenizer;
1212
import java.io.BufferedWriter;
1313
import java.io.OutputStreamWriter;
14+
import java.nio.charset.StandardCharsets;
1415

1516
// https://contest.yandex.ru/contest/24414/run-report/160043341/
1617

@@ -19,6 +20,9 @@ class FindSystem {
1920
private static final int MAX_DOCUMENTS = 10_000;
2021
private static final int MAX_QUERIES = 10_000;
2122
private static final int MAX_LINE_LENGTH = 10_000;
23+
private static final long MAX_TOTAL_DOCUMENT_CHARS = 2_000_000;
24+
private static final long MAX_TOTAL_QUERY_CHARS = 2_000_000;
25+
private static final long MAX_POSTING_VISITS = 20_000_000;
2226

2327
/*
2428
* Принцип работы алгоритма:
@@ -85,6 +89,17 @@ private static HashMap<String, ArrayList<int[]>> buildIndex(String[] docs) {
8589
}
8690

8791
private static String processQuery(String query, HashMap<String, ArrayList<int[]>> index) {
92+
try {
93+
return processQuery(query, index, Long.MAX_VALUE).output();
94+
} catch (IOException impossible) {
95+
throw new AssertionError(impossible);
96+
}
97+
}
98+
99+
private static QueryResult processQuery(
100+
String query,
101+
HashMap<String, ArrayList<int[]>> index,
102+
long postingVisitBudget) throws IOException {
88103
HashSet<String> uniqueWords = new HashSet<>();
89104
StringTokenizer st = new StringTokenizer(query);
90105

@@ -93,12 +108,17 @@ private static String processQuery(String query, HashMap<String, ArrayList<int[]
93108
}
94109

95110
HashMap<Integer, Integer> relevance = new HashMap<>();
111+
long postingVisits = 0;
96112

97113
for (String word : uniqueWords) {
98114
ArrayList<int[]> docs = index.get(word);
99115
if (docs == null) {
100116
continue;
101117
}
118+
if (docs.size() > postingVisitBudget - postingVisits) {
119+
throw new IOException("Aggregate query workload exceeds limit");
120+
}
121+
postingVisits += docs.size();
102122

103123
for (int[] pair : docs) {
104124
int docId = pair[0];
@@ -134,7 +154,10 @@ private static String processQuery(String query, HashMap<String, ArrayList<int[]
134154
sb.append(best.get(i)[0]);
135155
}
136156

137-
return sb.toString();
157+
return new QueryResult(sb.toString(), postingVisits);
158+
}
159+
160+
private record QueryResult(String output, long postingVisits) {
138161
}
139162

140163
private static boolean isBetter(int docId1, int score1, int docId2, int score2) {
@@ -149,18 +172,32 @@ private static void solve() throws Exception {
149172

150173
int n = reader.nextInt(MAX_DOCUMENTS);
151174
String[] docs = new String[n];
175+
long documentChars = 0;
152176
for (int i = 0; i < n; i++) {
153177
docs[i] = reader.nextLine(MAX_LINE_LENGTH);
178+
documentChars += docs[i].length();
179+
if (documentChars > MAX_TOTAL_DOCUMENT_CHARS) {
180+
throw new IOException("Aggregate document input exceeds limit");
181+
}
154182
}
155183

156184
HashMap<String, ArrayList<int[]>> index = buildIndex(docs);
157185

158186
int m = reader.nextInt(MAX_QUERIES);
159-
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
187+
BufferedWriter out = new BufferedWriter(
188+
new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
189+
long queryChars = 0;
190+
long postingVisitsLeft = MAX_POSTING_VISITS;
160191

161192
for (int i = 0; i < m; i++) {
162193
String query = reader.nextLine(MAX_LINE_LENGTH);
163-
out.write(processQuery(query, index));
194+
queryChars += query.length();
195+
if (queryChars > MAX_TOTAL_QUERY_CHARS) {
196+
throw new IOException("Aggregate query input exceeds limit");
197+
}
198+
QueryResult result = processQuery(query, index, postingVisitsLeft);
199+
postingVisitsLeft -= result.postingVisits();
200+
out.write(result.output());
164201
out.newLine();
165202
}
166203

@@ -212,11 +249,7 @@ public static void main(String[] args) throws Exception {
212249
if (System.getProperty("os.name").startsWith("Windows")) {
213250
test();
214251
} else {
215-
try {
216-
solve();
217-
} catch (IOException ignored) {
218-
// Invalid or excessive input is rejected without exhausting memory or CPU.
219-
}
252+
solve();
220253
}
221254
}
222255
private static class FastReader {

src/main/java/algorithms/sprint4/Map.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.io.IOException;
66
import java.io.InputStream;
77
import java.io.OutputStreamWriter;
8+
import java.nio.charset.StandardCharsets;
89
import java.util.OptionalInt;
910
import java.util.concurrent.ThreadLocalRandom;
1011

@@ -212,7 +213,8 @@ public static void main(String[] args) throws Exception {
212213
private static void solve() throws IOException {
213214
Reader reader = new Reader();
214215
HashTable table = new HashTable();
215-
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
216+
BufferedWriter out = new BufferedWriter(
217+
new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
216218

217219
int n = reader.nextInt(0, MAX_COMMANDS);
218220

src/main/java/algorithms/sprint5/PyramidSort.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -145,28 +145,33 @@ int nextInt() throws IOException {
145145
}
146146
} while (c <= ' ');
147147

148-
int sign = 1;
149-
if (c == '-') {
150-
sign = -1;
148+
boolean negative = c == '-';
149+
if (negative) {
151150
c = read();
152151
if (c <= ' ') {
153152
throw new NumberFormatException("Expected digit after sign");
154153
}
155154
}
156155

157-
int val = 0;
156+
int limit = negative ? Integer.MIN_VALUE : -Integer.MAX_VALUE;
157+
int multiplyLimit = limit / 10;
158+
int value = 0;
158159
while (c > ' ') {
159160
if (c < '0' || c > '9') {
160161
throw new NumberFormatException("Invalid integer input");
161162
}
162163
int digit = c - '0';
163-
if (val > (Integer.MAX_VALUE - digit) / 10) {
164-
throw new NumberFormatException("Integer input is too large");
164+
if (value < multiplyLimit) {
165+
throw new NumberFormatException("Integer input is out of range");
165166
}
166-
val = val * 10 + digit;
167+
value *= 10;
168+
if (value < limit + digit) {
169+
throw new NumberFormatException("Integer input is out of range");
170+
}
171+
value -= digit;
167172
c = read();
168173
}
169-
return val * sign;
174+
return negative ? value : -value;
170175
}
171176

172177
String next() throws IOException {

src/main/java/algorithms/sprint6/DorogayaSet.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -181,21 +181,35 @@ int nextInt() throws IOException {
181181
}
182182
} while (c <= ' ');
183183

184-
int sign = 1;
185-
186-
if (c == '-') {
187-
sign = -1;
184+
boolean negative = c == '-';
185+
if (negative) {
188186
c = read();
189187
}
190188

191-
int val = 0;
192-
189+
int limit = negative ? Integer.MIN_VALUE : -Integer.MAX_VALUE;
190+
int multiplyLimit = limit / 10;
191+
int value = 0;
192+
boolean hasDigit = false;
193193
while (c > ' ') {
194-
val = val * 10 + c - '0';
194+
if (c < '0' || c > '9') {
195+
throw new NumberFormatException("Invalid integer input");
196+
}
197+
int digit = c - '0';
198+
if (value < multiplyLimit) {
199+
throw new NumberFormatException("Integer input is out of range");
200+
}
201+
value *= 10;
202+
if (value < limit + digit) {
203+
throw new NumberFormatException("Integer input is out of range");
204+
}
205+
value -= digit;
206+
hasDigit = true;
195207
c = read();
196208
}
197-
198-
return val * sign;
209+
if (!hasDigit) {
210+
throw new NumberFormatException("Expected integer");
211+
}
212+
return negative ? value : -value;
199213
}
200214
}
201215

src/main/java/algorithms/sprint7/LevenshteinDistance.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,20 +108,25 @@ String nextLine(int maxLength) throws IOException {
108108
}
109109

110110
while (c != -1 && c != '\n') {
111-
if (c != '\r') {
112-
if (size == maxLength) {
113-
throw new IOException("Input line is too long");
114-
}
115-
if (size == tmp.length) {
116-
byte[] grown = new byte[Math.min(tmp.length * 2, maxLength)];
117-
System.arraycopy(tmp, 0, grown, 0, tmp.length);
118-
tmp = grown;
119-
}
120-
tmp[size++] = (byte) c;
111+
if (size == maxLength + 1) {
112+
throw new IOException("Input line is too long");
121113
}
114+
if (size == tmp.length) {
115+
byte[] grown = new byte[Math.min(tmp.length * 2, maxLength + 1)];
116+
System.arraycopy(tmp, 0, grown, 0, tmp.length);
117+
tmp = grown;
118+
}
119+
tmp[size++] = (byte) c;
122120
c = read();
123121
}
124122

123+
if (size > 0 && tmp[size - 1] == '\r') {
124+
size--;
125+
}
126+
if (size > maxLength) {
127+
throw new IOException("Input line is too long");
128+
}
129+
125130
return new String(tmp, 0, size, StandardCharsets.UTF_8);
126131
}
127132
}

0 commit comments

Comments
 (0)