{ }codelper
n줄 읽기한 줄 읽기문자열 나누기P0295

편지 묶음 간추리기

편지 n통이 한 파일에 이어 붙어 있다. 편지 하나는 머리 줄 한 줄, 본문 k줄, 첨부 a줄이 이 차례로 이어진 것이다. 머리 줄은 "이름 k a" 꼴이다.
이름은 보낸 사람이고, k는 본문 줄 수, a는 첨부 줄 수이다. 본문 줄은 쓴 그대로의 한 줄이다.
영어 글자, 숫자, 빈칸, 그리고 .
, !
?
로만 이루어지고 길이는 0자부터 80자까지이다. 본문 줄은 아무 글자도 없는 빈 줄일 수 있고, 빈칸으로 시작하거나 끝날 수 있고, 빈칸이 여러 개 이어질 수 있고, 빈칸만 있을 수도 있다. 본문 줄이 머리 줄이나 수처럼 생겼더라도 본문이다.
어디까지가 본문인지는 k로만 정해진다. 첨부 줄은 "파일이름 크기" 꼴이다. 본문에서 낱말은 빈칸이 아닌 글자가 이어진 한 덩어리를 말한다. 편지마다 다음 네 가지를 구하라. 첫째, 낱말이 하나라도 있는 본문 줄의 수. 둘째, 본문 전체의 낱말 수. 셋째, 가장 긴 본문 줄의 길이.
길이에는 앞뒤 빈칸을 포함해 그 줄의 모든 글자를 센다.
본문이 없으면 0이다. 넷째, 첨부 크기의 합.
첨부가 없으면 0이다. 편지가 주어진 차례대로 한 줄에 하나씩, 이름과 위의 네 수를 빈칸 하나로 띄워 출력하라.

입력
첫 줄에 n이 주어진다.
이어서 편지 n통이 차례로 주어진다.
머리 줄의 이름, k, a와 첨부 줄의 파일이름, 크기는 빈칸 하나로 나뉘고 줄 앞뒤에 빈칸이 없다.
1 ≤ n ≤ 1,000, 0 ≤ k ≤ 100, 0 ≤ a ≤ 20, 1 ≤ 크기 ≤ 1,000,000.
이름은 영어 소문자 1~10자, 파일이름은 영어 소문자·숫자·마침표로 된 1~20자이다.
입력은 모두 합쳐 10,000줄 이하이다
출력
편지마다 한 줄씩 n줄.
이름, 낱말이 있는 본문 줄 수, 낱말 수, 가장 긴 본문 줄의 길이, 첨부 크기의 합
예시 1 · stdin
3
kim 3 2
Hello there, team.

  see you at 9
plan.txt 1200
map.png 30500
lee 0 0
park 2 1
lee 0 0
7
notes.md 8
stdout
kim 2 7 18 31700
lee 0 0 0 0
park 2 4 7 8
예시 2 · stdin
2
choi 3 0
a  b   c

ok
yun 1 1
12
photo.png 1000000
stdout
choi 2 4 8 0
yun 1 1 2 1000000
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

줄 수를 먼저 알려 주고 그만큼 줄을 읽게 하는 입력이 두 겹으로 겹쳐 있어요: n통을 돌면서 통마다 k줄, a줄을 읽어요.
입력을 빈칸으로 한꺼번에 쪼개면 안 돼요.
본문은 줄 그대로가 필요하고, 첫 예시의 lee 0 0처럼 머리 줄같이 생긴 본문도 있어서 세어 가며 읽어야만 구별돼요.
한 줄을 통째로 읽는 방법은 Python input(), Java BufferedReader.readLine(), C# Console.ReadLine(), C++ getline(cin, line), C fgets, 크레이콘 콘솔.줄읽기()예요.
JavaScript·TypeScript는 입력 전체를 split("\n")으로 줄 배열로 만들고 자리 번호를 하나씩 올려요.
함정 하나: 수를 읽는 방법과 줄을 읽는 방법을 섞으면 줄이 밀려요.
C의 scanf("%d"), C++의 cin >>, Java Scanner의 nextInt()는 수 뒤의 줄바꿈을 남겨서, 바로 다음 fgets·getline·nextLine()이 빈 줄을 읽어요.
그래서 수가 든 줄도 한 줄로 읽은 다음 sscanf, istringstream, split으로 꺼내요.
함정 둘: 빈 줄도 본문 한 줄이에요.
빈 줄을 걸러 내고 읽으면 그 뒤가 한 줄씩 밀려요.
함정 셋: JavaScript에서 입력 전체에 trim()을 하면 마지막 편지의 본문이 빈 줄로 끝날 때 그 줄이 사라져요.
함정 넷: 줄 길이에는 앞뒤 빈칸도 들어가요.
Python strip(), JavaScript trim(), 크레이콘 공백제거()를 한 다음에 길이를 재면 짧아져요(첫 예시의 see you at 9는 14자예요).
함정 다섯: 낱말을 셀 때 빈칸 하나로 쪼개면 빈칸이 이어진 곳에서 빈 문자열이 나와요.
JavaScript "a b".split(" ")는 3조각이고, C# Split(' ')도 그래서 StringSplitOptions.RemoveEmptyEntries를 줘요.
Python split()과 크레이콘 나누기()는 기준을 주지 않으면 이어진 빈칸을 하나로 보고 낱말만 돌려줘요.
Java는 StringTokenizer의 countTokens(), C++는 istringstream에서 >>로 꺼낸 횟수, C는 빈칸 다음에 글자가 오는 자리를 세요.
함정 여섯: 빈칸만 있는 줄은 빈 문자열이 아니지만 낱말이 없는 줄이에요.
line != ""로 견주면 틀리고, 낱말 수가 0인지 봐야 해요.
함정 일곱: C의 fgets(line, 81, stdin)은 80자까지만 읽어요.
80자짜리 줄이면 줄바꿈이 남아 다음 fgets가 빈 줄을 읽으니, 배열에는 줄바꿈과 끝 표시 '\0'의 자리까지 있어야 해요.

Pythonmain.py · 18 줄
n = int(input())out = []for _ in range(n):    # 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a    name, k, a = input().split()    filled = words = longest = 0    for _ in range(int(k)):        line = input()  # 줄 끝의 줄바꿈만 떼고 앞뒤 빈칸은 그대로 둔다        count = len(line.split())  # split()은 빈칸이 이어져도 낱말만 돌려준다        if count > 0:            filled += 1        words += count        longest = max(longest, len(line))    total = 0    for _ in range(int(a)):        total += int(input().split()[1])    out.append(f"{name} {filled} {words} {longest} {total}")print("\n".join(out))
JavaScriptmain.js · 24 줄
// 줄 단위로 읽어야 하니 빈칸으로 쪼개지 않는다. 통째로 trim()하지도 않는다: 끝의 빈 줄도 본문일 수 있다const lines = require("fs").readFileSync(0, "utf8").split("\n");let at = 0;const n = Number(lines[at++]);const out = [];for (let i = 0; i < n; i++) {  // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a  const [name, k, a] = lines[at++].split(" ");  let filled = 0;  let words = 0;  let longest = 0;  for (let j = 0; j < Number(k); j++) {    const line = lines[at++];    // split(" ")은 빈칸이 이어지면 빈 문자열을 돌려주니 걸러 낸다    const count = line.split(" ").filter((w) => w !== "").length;    if (count > 0) filled++;    words += count;    longest = Math.max(longest, line.length);  }  let total = 0;  for (let j = 0; j < Number(a); j++) total += Number(lines[at++].split(" ")[1]);  out.push(`${name} ${filled} ${words} ${longest} ${total}`);}console.log(out.join("\n"));
TypeScriptmain.ts · 26 줄
import { readFileSync } from "node:fs"; // 줄 단위로 읽어야 하니 빈칸으로 쪼개지 않는다. 통째로 trim()하지도 않는다: 끝의 빈 줄도 본문일 수 있다const lines: string[] = readFileSync(0, "utf8").split("\n");let at = 0;const n = Number(lines[at++]);const out: string[] = [];for (let i = 0; i < n; i++) {  // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a  const [name, k, a] = lines[at++].split(" ");  let filled = 0;  let words = 0;  let longest = 0;  for (let j = 0; j < Number(k); j++) {    const line: string = lines[at++];    // split(" ")은 빈칸이 이어지면 빈 문자열을 돌려주니 걸러 낸다    const count = line.split(" ").filter((w) => w !== "").length;    if (count > 0) filled++;    words += count;    longest = Math.max(longest, line.length);  }  let total = 0;  for (let j = 0; j < Number(a); j++) total += Number(lines[at++].split(" ")[1]);  out.push(`${name} ${filled} ${words} ${longest} ${total}`);}console.log(out.join("\n"));
JavaMain.java · 31 줄
import java.io.*;import java.util.*; public class Main {    public static void main(String[] args) throws IOException {        // readLine은 줄바꿈만 떼고 한 줄을 통째로 돌려준다: 앞뒤 빈칸도 빈 줄도 그대로다        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(br.readLine());        StringBuilder sb = new StringBuilder();        for (int i = 0; i < n; i++) {            // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a            String[] head = br.readLine().split(" ");            int k = Integer.parseInt(head[1]);            int a = Integer.parseInt(head[2]);            int filled = 0, words = 0, longest = 0;            for (int j = 0; j < k; j++) {                String line = br.readLine();                // StringTokenizer는 빈칸이 이어져도 낱말만 센다                int count = new StringTokenizer(line).countTokens();                if (count > 0) filled++;                words += count;                longest = Math.max(longest, line.length());            }            int total = 0;            for (int j = 0; j < a; j++) total += Integer.parseInt(br.readLine().split(" ")[1]);            sb.append(head[0]).append(' ').append(filled).append(' ').append(words).append(' ')              .append(longest).append(' ').append(total).append('\n');        }        System.out.print(sb);    }}
C#Main.cs · 32 줄
using System;using System.Text; class Program{    static void Main()    {        // ReadLine은 줄바꿈만 떼고 한 줄을 통째로 돌려준다: 앞뒤 빈칸도 빈 줄도 그대로다        int n = int.Parse(Console.ReadLine()!);        var sb = new StringBuilder();        for (int i = 0; i < n; i++)        {            // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a            string[] head = Console.ReadLine()!.Split(' ');            int k = int.Parse(head[1]), a = int.Parse(head[2]);            int filled = 0, words = 0, longest = 0;            for (int j = 0; j < k; j++)            {                string line = Console.ReadLine()!;                // RemoveEmptyEntries가 없으면 빈칸이 이어질 때 빈 문자열이 낱말로 세어진다                int count = line.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length;                if (count > 0) filled++;                words += count;                longest = Math.Max(longest, line.Length);            }            int total = 0;            for (int j = 0; j < a; j++) total += int.Parse(Console.ReadLine()!.Split(' ')[1]);            sb.Append($"{head[0]} {filled} {words} {longest} {total}\n");        }        Console.Write(sb);    }}
C++main.cpp · 45 줄
#include <algorithm>#include <iostream>#include <sstream>#include <string>using namespace std; int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    // 모든 줄을 getline으로 읽는다: cin >> 와 섞으면 >> 가 남긴 줄바꿈 때문에 getline이 빈 줄을 읽는다    string line;    getline(cin, line);    int n = stoi(line);    for (int i = 0; i < n; i++) {        // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a — 읽은 줄을 istringstream에 넣어 >> 로 꺼낸다        getline(cin, line);        istringstream head(line);        string name;        int k, a;        head >> name >> k >> a;        int filled = 0, words = 0, longest = 0;        for (int j = 0; j < k; j++) {            getline(cin, line);            // >> 는 빈칸이 이어져도 낱말만 하나씩 꺼낸다            istringstream body(line);            string word;            int count = 0;            while (body >> word) count++;            if (count > 0) filled++;            words += count;            longest = max(longest, (int)line.size());        }        int total = 0;        for (int j = 0; j < a; j++) {            getline(cin, line);            istringstream file(line);            string fileName;            int size;            file >> fileName >> size;            total += size;        }        cout << name << ' ' << filled << ' ' << words << ' ' << longest << ' ' << total << '\n';    }    return 0;}
Cmain.c · 39 줄
#include <stdio.h>#include <string.h> int main(void) {    // 한 줄은 80자까지지만 줄바꿈과 끝 표시('\0')까지 들어갈 자리가 있어야 한다    char line[128];    int n;    // 수도 fgets로 한 줄을 통째로 읽고 sscanf로 꺼낸다: scanf("%d")는 줄바꿈을 남겨 다음 fgets가 빈 줄을 읽는다    if (!fgets(line, sizeof line, stdin)) return 0;    sscanf(line, "%d", &n);    for (int i = 0; i < n; i++) {        // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a        char name[11];        int k = 0, a = 0;        fgets(line, sizeof line, stdin);        sscanf(line, "%10s %d %d", name, &k, &a);        int filled = 0, words = 0, longest = 0;        for (int j = 0; j < k; j++) {            fgets(line, sizeof line, stdin);            int len = (int)strcspn(line, "\n"); // 줄바꿈 앞까지의 길이: 앞뒤 빈칸은 그대로 센다            // 빈칸 다음에 빈칸이 아닌 글자가 나올 때마다 낱말 하나가 시작된다            int count = 0;            for (int c = 0; c < len; c++)                if (line[c] != ' ' && (c == 0 || line[c - 1] == ' ')) count++;            if (count > 0) filled++;            words += count;            if (len > longest) longest = len;        }        int total = 0;        for (int j = 0; j < a; j++) {            int size = 0;            fgets(line, sizeof line, stdin);            sscanf(line, "%*s %d", &size); // %*s: 파일 이름은 읽고 버린다            total += size;        }        printf("%s %d %d %d %d\n", name, filled, words, longest, total);    }    return 0;}
Craykonmain.cray · 29 줄
// 줄읽기는 줄바꿈만 떼고 한 줄을 통째로 돌려준다: 앞뒤 빈칸도 빈 줄도 그대로다n = 콘솔.줄읽기().수로변환();반복 (i = 0; i < n; i++){    // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a    head = 콘솔.줄읽기().나누기();    k = head[1].수로변환();    a = head[2].수로변환();    filled = 0;    words = 0;    longest = 0;    반복 (j = 0; j < k; j++)    {        line = 콘솔.줄읽기();        count = line.나누기().개수; // 기준 없이 나누면 빈칸이 이어져도 낱말만 돌려준다        만약 (count > 0)        {            filled += 1;        }        words += count;        longest = 수학.큰수(longest, line.길이);    }    total = 0;    반복 (j = 0; j < a; j++)    {        total += 콘솔.줄읽기().나누기()[1].수로변환();    }    콘솔.쓰기(head[0] + " " + filled + " " + words + " " + longest + " " + total);}