In a server log, secret values such as passwords are written between [[ and ]].
You will print the log again with the characters between them hidden by asterisks (*).
The log has several lines, and a line that is exactly END marks the end.
The END line and every line after it are neither processed nor printed.
Process one line like this.
First set the search start to the beginning of the line.
Find the first [[ at or after the search start. If there is none, the line is done.
Find the first ]] starting right after that [[. If there is none, the line is done (text after an unclosed [[ stays as it is).
Replace every character between that [[ and ]] with *. The number of characters stays the same, and [[ and ]] themselves remain.
Even with nothing between them ([[]]) it counts as one hidden section.
Set the new search start right after that ]] and look for [[ again.
Print the processed lines in input order, then on the last line print the number of hidden sections and the number of characters changed to *, separated by one space.
Input
There are 0 to 100 log lines before the END line. Each log line has length 1 to 200, uses only ASCII characters (codes 32–126), and does not start or end with a space. The END line is always present
Output
One line per log line, then one line "sections characters"
Example 1 · stdin
login user=kim pass=[[hunter2]] ok
token [[]] and [[abc
END
stdout
login user=kim pass=[[*******]] ok
token [[]] and [[abc
2 7
Example 2 · stdin
]] [[x]] [[a[[b]]c]]
ENDING [[ok]]
END
after [[no]]
stdout
]] [[*]] [[****]]c]]
ENDING [[**]]
3 7
main.py
Error List00F8 ↓ · ⇧F8 ↑
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
The key is where each search starts. Search for ]] from right after the [[ you just found, not from the start of the line; otherwise, when ]] appears before [[ (first line of the second example), you pair the wrong marks. Search for the next [[ from right after ]], so the inner [[ in "[[a[[b]]c]]" is not counted as a new section. Searching from a position: Python s.find(t, pos), JS/TS/Java s.indexOf(t, pos), C# s.IndexOf(t, pos, StringComparison.Ordinal), C++ s.find(t, pos). When not found, Python/JS/TS/Java/C# return -1 and C++ returns std::string::npos. C# IndexOf with a string argument uses culture-sensitive comparison by default, so adding Ordinal is safer. C strstr(p, t) returns a pointer instead of a position, or NULL, so you move the pointer forward as you search. Craykon IndexOf (자리찾기) takes no start position, so cut first with line.Substring(pos).IndexOf("[[") and add pos back. n asterisks: Python "*" * n, JS/TS/Java "*".repeat(n), C# new string('*', n), C++ std::string(n, '*'), Craykon "*".Repeat(n) (되풀이). In C the length does not change, so replace in place. The end marker is only a line that is exactly END. Testing startsWith("END") stops at the ENDING line, and reading to end of input also counts lines after END. In Java, line == "END" asks whether they are the same object, so it is always false for a line you read; compare with line.equals("END"). Masking to the end of the line after an unclosed [[, or not counting an empty [[]], are also common mistakes.
Pythonmain.py · 25 lines
import syssections = 0masked = 0out = []line = sys.stdin.readline().rstrip("\n")# END 줄이 끝 표시다. 그 뒤의 줄은 읽지 않는다while line != "END": pos = 0 while True: i = line.find("[[", pos) if i < 0: break j = line.find("]]", i + 2) # 여는 [[ 바로 뒤부터 찾는다 if j < 0: break # 닫히지 않은 [[ 뒤는 그대로 둔다 n = j - (i + 2) line = line[: i + 2] + "*" * n + line[j:] sections += 1 masked += n pos = j + 2 out.append(line) line = sys.stdin.readline().rstrip("\n")out.append(f"{sections} {masked}")print("\n".join(out))
JavaScriptmain.js · 23 lines
const lines = require("fs").readFileSync(0, "utf8").split("\n");let sections = 0;let masked = 0;const out = [];// END 줄이 끝 표시다. 그 뒤의 줄은 보지 않는다for (let k = 0; lines[k] !== "END"; k++) { let line = lines[k]; let pos = 0; while (true) { const i = line.indexOf("[[", pos); if (i < 0) break; const j = line.indexOf("]]", i + 2); // 여는 [[ 바로 뒤부터 찾는다 if (j < 0) break; // 닫히지 않은 [[ 뒤는 그대로 둔다 const n = j - (i + 2); line = line.slice(0, i + 2) + "*".repeat(n) + line.slice(j); sections++; masked += n; pos = j + 2; } out.push(line);}out.push(`${sections} ${masked}`);console.log(out.join("\n"));
TypeScriptmain.ts · 24 lines
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");let sections = 0;let masked = 0;const out: string[] = [];// END 줄이 끝 표시다. 그 뒤의 줄은 보지 않는다for (let k = 0; lines[k] !== "END"; k++) { let line: string = lines[k]; let pos = 0; while (true) { const i: number = line.indexOf("[[", pos); if (i < 0) break; const j: number = line.indexOf("]]", i + 2); // 여는 [[ 바로 뒤부터 찾는다 if (j < 0) break; // 닫히지 않은 [[ 뒤는 그대로 둔다 const n: number = j - (i + 2); line = line.slice(0, i + 2) + "*".repeat(n) + line.slice(j); sections++; masked += n; pos = j + 2; } out.push(line);}out.push(`${sections} ${masked}`);console.log(out.join("\n"));
JavaMain.java · 31 lines
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int sections = 0, masked = 0; StringBuilder out = new StringBuilder(); String line = br.readLine(); // END 줄이 끝 표시다. 문자열 비교는 equals로 한다 while (!line.equals("END")) { int pos = 0; while (true) { int i = line.indexOf("[[", pos); if (i < 0) break; int j = line.indexOf("]]", i + 2); // 여는 [[ 바로 뒤부터 찾는다 if (j < 0) break; // 닫히지 않은 [[ 뒤는 그대로 둔다 int n = j - (i + 2); line = line.substring(0, i + 2) + "*".repeat(n) + line.substring(j); sections++; masked += n; pos = j + 2; } out.append(line).append('\n'); line = br.readLine(); } out.append(sections).append(' ').append(masked).append('\n'); System.out.print(out); }}
C#Main.cs · 34 lines
using System;using System.Text;class Program{ static void Main() { int sections = 0, masked = 0; var sb = new StringBuilder(); string line = Console.ReadLine(); // END 줄이 끝 표시다. 그 뒤의 줄은 읽지 않는다 while (line != "END") { int pos = 0; while (true) { // string으로 찾으면 문화권 비교가 되므로 Ordinal을 준다 int i = line.IndexOf("[[", pos, StringComparison.Ordinal); if (i < 0) break; int j = line.IndexOf("]]", i + 2, StringComparison.Ordinal); // 여는 [[ 바로 뒤부터 if (j < 0) break; // 닫히지 않은 [[ 뒤는 그대로 둔다 int n = j - (i + 2); line = line.Substring(0, i + 2) + new string('*', n) + line.Substring(j); sections++; masked += n; pos = j + 2; } sb.Append(line).Append('\n'); line = Console.ReadLine(); } sb.Append(sections).Append(' ').Append(masked).Append('\n'); Console.Write(sb); }}
C++main.cpp · 24 lines
#include <iostream>#include <string>int main() { std::string line; int sections = 0, masked = 0; // END 줄이 끝 표시다. 그 뒤의 줄은 읽지 않는다 while (std::getline(std::cin, line) && line != "END") { std::size_t pos = 0; while (true) { std::size_t i = line.find("[[", pos); if (i == std::string::npos) break; std::size_t j = line.find("]]", i + 2); // 여는 [[ 바로 뒤부터 찾는다 if (j == std::string::npos) break; // 닫히지 않은 [[ 뒤는 그대로 둔다 std::size_t n = j - (i + 2); line.replace(i + 2, n, std::string(n, '*')); sections++; masked += static_cast<int>(n); pos = j + 2; } std::cout << line << '\n'; } std::cout << sections << ' ' << masked << '\n';}
Cmain.c · 27 lines
#include <stdio.h>#include <string.h>int main(void) { char line[256]; int sections = 0, masked = 0; /* END 줄이 끝 표시다. 그 뒤의 줄은 읽지 않는다 */ while (fgets(line, sizeof line, stdin)) { line[strcspn(line, "\r\n")] = '\0'; if (strcmp(line, "END") == 0) break; char *pos = line; while (1) { char *open = strstr(pos, "[["); if (open == NULL) break; char *close = strstr(open + 2, "]]"); /* 여는 [[ 바로 뒤부터 찾는다 */ if (close == NULL) break; /* 닫히지 않은 [[ 뒤는 그대로 둔다 */ /* 글자 수가 같으니 제자리에서 바꾼다 */ for (char *p = open + 2; p < close; p++) *p = '*'; sections++; masked += (int)(close - (open + 2)); pos = close + 2; } puts(line); } printf("%d %d\n", sections, masked); return 0;}
Craykonmain.cray · 32 lines
구간수 = 0;가린수 = 0;줄 = Console.ReadLine();// END 줄이 끝 표시다. 그 뒤의 줄은 읽지 않는다while (줄 != "END"){ 위치 = 0; while (true) { // 자리찾기에는 시작 자리를 줄 수 없어서, 잘라 낸 뒤 찾고 떼어 낸 만큼 더한다 i = 줄.Substring(위치).IndexOf("[["); if (i < 0) { break; } i += 위치; j = 줄.Substring(i + 2).IndexOf("]]"); // 여는 [[ 바로 뒤부터 찾는다 if (j < 0) { break; // 닫히지 않은 [[ 뒤는 그대로 둔다 } j += i + 2; n = j - (i + 2); 줄 = 줄.Substring(0, i + 2) + "*".Repeat(n) + 줄.Substring(j); 구간수 += 1; 가린수 += n; 위치 = j + 2; } Console.WriteLine(줄); 줄 = Console.ReadLine();}Console.WriteLine(구간수 + " " + 가린수);