서버 로그에는 비밀번호 같은 비밀 값이 [[ 와 ]] 사이에 적혀 있다.
이 사이의 글자를 별표(*)로 가려서 로그를 다시 출력하려고 한다.
로그는 여러 줄이고, 내용이 정확히 END인 줄이 끝 표시다.
END 줄과 그 뒤의 줄은 처리하지도 출력하지도 않는다.
한 줄은 이렇게 처리한다.
먼저 찾기 시작 자리를 줄의 맨 앞으로 둔다.
시작 자리부터 처음 나오는 [[ 를 찾는다. 없으면 그 줄의 처리는 끝난다.
그 [[ 바로 뒤부터 처음 나오는 ]] 를 찾는다. 없으면 그 줄의 처리는 끝난다(닫히지 않은 [[ 뒤는 그대로 둔다).
찾은 [[ 와 ]] 사이의 글자를 하나도 빠짐없이 * 로 바꾼다. 글자 수는 그대로이고, [[ 와 ]] 자체는 남긴다.
사이에 글자가 없어도([[]]) 가린 구간 하나로 친다.
그 ]] 바로 뒤를 새 시작 자리로 두고 다시 [[ 를 찾는다.
처리한 줄을 입력 차례대로 출력한 뒤, 마지막 줄에 가린 구간의 수와 * 로 바꾼 글자의 수를 빈칸 하나로 띄어 출력한다.
입력
END 줄 앞의 로그 줄은 0 ~ 100개다. 로그 줄마다 길이 1 ~ 200이고 ASCII 글자(코드 32~126)만 쓰며, 맨 앞과 맨 끝은 빈칸이 아니다. END 줄은 꼭 있다
출력
로그 줄 수만큼의 줄, 그리고 "구간 수 글자 수" 한 줄
예시 1 · stdin
login user=kim pass=[[hunter2]] ok
token [[]] and [[abc
END
stdout
login user=kim pass=[[*******]] ok
token [[]] and [[abc
2 7
예시 2 · stdin
]] [[x]] [[a[[b]]c]]
ENDING [[ok]]
END
after [[no]]
stdout
]] [[*]] [[****]]c]]
ENDING [[**]]
3 7
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이
핵심은 '어디부터 찾느냐'예요. ]] 는 줄 맨 앞이 아니라 방금 찾은 [[ 바로 뒤부터 찾아야 해요. 그러지 않으면 두 번째 예시의 첫 줄처럼 ]] 가 [[ 보다 앞에 있을 때 엉뚱한 짝이 잡혀요. 다음 [[ 도 ]] 바로 뒤부터 찾아야 "[[a[[b]]c]]"의 안쪽 [[ 를 새 구간으로 세지 않아요. 시작 자리를 주는 찾기는 Python s.find(t, pos), JS·TS·Java s.indexOf(t, pos), C# s.IndexOf(t, pos, StringComparison.Ordinal), C++ s.find(t, pos)예요. 없을 때 Python·JS·TS·Java·C#은 -1, C++는 std::string::npos예요. C# IndexOf에 문자열을 주면 기본이 문화권 비교라서 Ordinal을 붙이는 게 안전해요. C strstr(p, t)는 자리 대신 포인터를 돌려주고 없으면 NULL이라, 포인터를 옮겨 가며 찾아요. 크레이콘 자리찾기에는 시작 자리를 줄 수 없어서 줄.부분문자열(위치).자리찾기("[[")로 잘라 찾은 뒤 위치를 더해요. 별표 n개는 Python "*" * n, JS·TS·Java "*".repeat(n), C# new string('*', n), C++ std::string(n, '*'), 크레이콘 "*".되풀이(n)이에요. C는 글자 수가 같으니 제자리에서 바꿔요. 끝 표시는 줄 전체가 END일 때만이에요. startsWith("END")로 물으면 ENDING 줄에서 멈추고, 입력 끝까지 읽으면 END 뒤의 줄까지 세요. Java에서 line == "END"는 같은 객체인지를 물어서 읽어 들인 줄과는 늘 다르다고 나오니, line.equals("END")로 비교해요. 닫히지 않은 [[ 뒤를 끝까지 가리거나 빈 [[]] 를 세지 않는 것도 흔한 실수예요.
Pythonmain.py · 25 줄
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 줄
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 줄
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 줄
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 줄
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 줄
#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 줄
#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 줄
구간수 = 0;가린수 = 0;줄 = 콘솔.줄읽기();// END 줄이 끝 표시다. 그 뒤의 줄은 읽지 않는다동안 (줄 != "END"){ 위치 = 0; 동안 (참) { // 자리찾기에는 시작 자리를 줄 수 없어서, 잘라 낸 뒤 찾고 떼어 낸 만큼 더한다 i = 줄.부분문자열(위치).자리찾기("[["); 만약 (i < 0) { 그만; } i += 위치; j = 줄.부분문자열(i + 2).자리찾기("]]"); // 여는 [[ 바로 뒤부터 찾는다 만약 (j < 0) { 그만; // 닫히지 않은 [[ 뒤는 그대로 둔다 } j += i + 2; n = j - (i + 2); 줄 = 줄.부분문자열(0, i + 2) + "*".되풀이(n) + 줄.부분문자열(j); 구간수 += 1; 가린수 += n; 위치 = j + 2; } 콘솔.쓰기(줄); 줄 = 콘솔.줄읽기();}콘솔.쓰기(구간수 + " " + 가린수);