{ }codelper
목록에 있나목록에 넣기P0237

요리법에 모자란 재료

냉장고에 있는 재료 목록과 요리법 r개가 주어진다. 첫 줄에 재료의 개수 m이, 둘째 줄에 재료 m개가 빈칸 하나씩으로 띄워 주어진다. 냉장고 목록에는 같은 재료가 여러 번 적혀 있을 수 있다. 셋째 줄에 r이 주어지고, 다음 r줄에 요리법이 한 줄에 하나씩 주어진다. 요리법 한 줄에는 요리 이름, 항목의 수 k, 항목 k개가 빈칸 하나씩으로 띄워져 있다. 항목은 재료 하나이거나, 재료 2~3개를 '/'로 이은 것이다(예: butter/oil). 재료 하나인 항목은 그 재료가 냉장고 목록에 있으면 갖춘 것이다. '/'로 이은 항목은 그 가운데 재료 하나라도 냉장고 목록에 있으면 갖춘 것이다. 재료는 이름이 통째로 같아야 같은 재료다.
그래서 egg가 있어도 eggplant는 없는 것이고, eggplant가 있어도 egg는 없는 것이다. 요리법마다 한 줄에, 요리 이름, 갖추지 못한 항목의 수, 갖추지 못한 항목들을 빈칸 하나씩으로 띄워 출력하라. 갖추지 못한 항목은 요리법에 적힌 그대로, 요리법에 처음 나온 차례대로 쓴다. 글자까지 똑같은 항목이 요리법에 여러 번 적혀 있으면 한 번만 쓰고 한 번만 센다. butter/oil과 oil/butter는 글자가 다르니 다른 항목이다. 갖추지 못한 항목이 없으면 요리 이름과 0만 출력한다.

입력
1 ≤ m ≤ 300, 1 ≤ r ≤ 300, 1 ≤ k ≤ 30, 재료와 요리 이름은 영어 소문자 1~12글자
출력
요리법마다 한 줄(요리 이름, 갖추지 못한 항목의 수, 그 항목들)
예시 1 · stdin
4
egg milk flour egg
2
pancake 5 egg milk butter/oil sugar butter/oil
omelet 2 egg milk
stdout
pancake 2 butter/oil sugar
omelet 0
예시 2 · stdin
2
eggplant oil
1
fry 4 egg butter/oil egg rice/egg
stdout
fry 2 egg rice/egg
main.py
입력(stdin)

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

$ 준비됐어요

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

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

목록에 같은 값이 있는지 묻는 방법: Python x in fridge, JS·TS fridge.includes(x), Java fridge.contains(x), C# fridge.Contains(x), C++ std::find(v.begin(), v.end(), x) != v.end(), 크레이콘 fridge.있나(x)예요.
C에는 이런 함수가 없어서 strcmp로 하나씩 비교하는 반복문을 직접 써요.
이 문제는 이 물음을 두 번 써요.
냉장고에 재료가 있는지 물을 때 한 번, 갖추지 못한 항목을 이미 적었는지 물을 때 한 번이에요.
둘째 물음을 빼면 butter/oil이 두 번 나와요.
첫째 함정은 목록과 문자열의 차이예요.
재료 줄을 나누지 않고 문자열 그대로 두고 Python x in line, JS·TS line.includes(x), C# line.Contains(x), 크레이콘 line.있나(x)로 물으면 부분 문자열을 찾아서 eggplant 속의 egg도 있다고 나와요.
꼭 나눈 목록에 물어요.
C에서 strncmp(a, b, strlen(b))로 비교해도 앞부분만 같은 낱말이 같다고 나와요.
Java에서 문자열을 ==로 비교하면 내용이 아니라 같은 객체인지를 봐서, split으로 만든 문자열은 내용이 같아도 다르다고 나와요.
contains·equals를 써요.
둘째 함정은 '/'의 뜻이에요.
재료 가운데 하나라도 있으면 되니 Python any, JS·TS some처럼 "하나라도"로 물어요.
"모두"(all·every)로 물으면 butter/oil이 oil만 있을 때 모자라다고 나와요.

Pythonmain.py · 13 줄
m = int(input())fridge = input().split()  # 재료 목록(문자열 그대로가 아니라 낱말의 목록)r = int(input())for _ in range(r):    parts = input().split()    dish, k, items = parts[0], int(parts[1]), parts[2:]    missing = []    for item in items:        # '/'로 이은 재료는 하나라도 목록에 있으면 갖춘 것        have = any(x in fridge for x in item.split("/"))        if not have and item not in missing:  # 같은 항목은 한 번만            missing.append(item)    print(" ".join([dish, str(len(missing))] + missing))
JavaScriptmain.js · 15 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const fridge = lines[1].trim().split(/\s+/); // 재료 배열const r = Number(lines[2]);const out = [];for (let i = 0; i < r; i++) {  const [dish, , ...items] = lines[3 + i].trim().split(/\s+/);  const missing = [];  for (const item of items) {    // 배열의 includes는 같은 낱말을 찾는다. '/'로 이은 것은 하나라도 있으면 된다    const have = item.split("/").some((x) => fridge.includes(x));    if (!have && !missing.includes(item)) missing.push(item);  }  out.push([dish, missing.length, ...missing].join(" "));}console.log(out.join("\n"));
TypeScriptmain.ts · 16 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const fridge: string[] = lines[1].trim().split(/\s+/); // 재료 배열const r: number = Number(lines[2]);const out: string[] = [];for (let i = 0; i < r; i++) {  const [dish, , ...items]: string[] = lines[3 + i].trim().split(/\s+/);  const missing: string[] = [];  for (const item of items) {    // 배열의 includes는 같은 낱말을 찾는다. '/'로 이은 것은 하나라도 있으면 된다    const have: boolean = item.split("/").some((x) => fridge.includes(x));    if (!have && !missing.includes(item)) missing.push(item);  }  out.push([dish, String(missing.length), ...missing].join(" "));}console.log(out.join("\n"));
JavaMain.java · 35 줄
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.Arrays;import java.util.List; public class Main {    public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        in.readLine();        List<String> fridge = Arrays.asList(in.readLine().trim().split(" "));        int r = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int i = 0; i < r; i++) {            String[] parts = in.readLine().trim().split(" ");            List<String> missing = new ArrayList<>();            for (int j = 2; j < parts.length; j++) {                String item = parts[j];                boolean have = false;                // List.contains는 equals로 비교한다(==는 같은 객체인지를 본다)                for (String x : item.split("/")) {                    if (fridge.contains(x)) {                        have = true;                        break;                    }                }                if (!have && !missing.contains(item)) missing.add(item);            }            out.append(parts[0]).append(' ').append(missing.size());            for (String x : missing) out.append(' ').append(x);            out.append('\n');        }        System.out.print(out);    }}
C#Main.cs · 34 줄
using System;using System.Collections.Generic;using System.Text; class Program{    static void Main()    {        Console.ReadLine();        var fridge = new List<string>(Console.ReadLine().Trim().Split(' '));        int r = int.Parse(Console.ReadLine());        var sb = new StringBuilder();        for (int i = 0; i < r; i++)        {            string[] parts = Console.ReadLine().Trim().Split(' ');            var missing = new List<string>();            for (int j = 2; j < parts.Length; j++)            {                string item = parts[j];                // List.Contains는 낱말이 통째로 같은지 본다. string.Contains는 부분 문자열을 찾는다                bool have = false;                foreach (string x in item.Split('/'))                {                    if (fridge.Contains(x)) { have = true; break; }                }                if (!have && !missing.Contains(item)) missing.Add(item);            }            sb.Append(parts[0]).Append(' ').Append(missing.Count);            foreach (string x in missing) sb.Append(' ').Append(x);            sb.Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 36 줄
#include <algorithm>#include <iostream>#include <sstream>#include <string>#include <vector> // 목록에 x와 똑같은 것이 있나(std::find는 ==로 비교하고, std::string의 ==는 내용을 본다)bool contains(const std::vector<std::string> &v, const std::string &x) {    return std::find(v.begin(), v.end(), x) != v.end();} int main() {    int m, r, k;    std::cin >> m;    std::vector<std::string> fridge(m);    for (auto &x : fridge) std::cin >> x;    std::cin >> r;    for (int i = 0; i < r; i++) {        std::string dish, item;        std::cin >> dish >> k;        std::vector<std::string> missing;        for (int j = 0; j < k; j++) {            std::cin >> item;            bool have = false;            std::stringstream ss(item);            std::string x;            while (std::getline(ss, x, '/')) {                if (contains(fridge, x)) { have = true; break; }            }            if (!have && !contains(missing, item)) missing.push_back(item);        }        std::cout << dish << ' ' << missing.size();        for (const auto &x : missing) std::cout << ' ' << x;        std::cout << '\n';    }}
Cmain.c · 49 줄
#include <stdio.h>#include <string.h> static char fridge[300][13];static int m; /* 목록에 같은 재료가 있나: 내용 비교는 strcmp(==는 주소를 비교한다) */int in_fridge(const char *w) {    for (int i = 0; i < m; i++) {        if (strcmp(fridge[i], w) == 0) return 1;    }    return 0;} /* '/'로 이은 재료 가운데 하나라도 있나 */int have_item(const char *item) {    char buf[40];    strcpy(buf, item);    for (char *x = strtok(buf, "/"); x; x = strtok(NULL, "/")) {        if (in_fridge(x)) return 1;    }    return 0;} int main(void) {    int r, k;    char dish[13], items[30][40];    scanf("%d", &m);    for (int i = 0; i < m; i++) scanf("%12s", fridge[i]);    scanf("%d", &r);    for (int i = 0; i < r; i++) {        scanf("%12s %d", dish, &k);        int nmiss = 0;        int miss[30];        for (int j = 0; j < k; j++) {            scanf("%39s", items[j]);            if (have_item(items[j])) continue;            int dup = 0; /* 이미 적은 항목인가 */            for (int t = 0; t < nmiss; t++) {                if (strcmp(items[miss[t]], items[j]) == 0) { dup = 1; break; }            }            if (!dup) miss[nmiss++] = j;        }        printf("%s %d", dish, nmiss);        for (int t = 0; t < nmiss; t++) printf(" %s", items[miss[t]]);        putchar('\n');    }    return 0;}
Craykonmain.cray · 32 줄
콘솔.줄읽기();fridge = 콘솔.줄읽기().나누기(); // 재료 목록r = 콘솔.줄읽기().수로변환();반복 (i = 0; i < r; i++){    parts = 콘솔.줄읽기().나누기();    missing = [];    반복 (j = 2; j < parts.개수; j++)    {        item = parts[j];        // 목록의 있나는 같은 낱말을 찾는다. '/'로 이은 것은 하나라도 있으면 된다        have = 거짓;        각각 (x 에서 item.나누기("/"))        {            만약 (fridge.있나(x))            {                have = 참;                그만;            }        }        만약 (!have && !missing.있나(item))        {            missing.넣기(item);        }    }    line = parts[0] + " " + missing.개수;    각각 (x 에서 missing)    {        line += " " + x;    }    콘솔.쓰기(line);}