{ }codelper
같음 비교와 형 변환P0287

자물쇠 기록 대조

자전거 보관소의 자물쇠에는 주인의 이름과 비밀 번호 여러 개가 차례대로 등록되어 있다. 기록 하나는 "이름 k 번호1 … 번호k" 꼴의 한 줄이다.
k는 번호의 개수다. 첫 줄에 등록된 기록이 주어진다. 둘째 줄에 시도 수 n이, 다음 n줄에 사람들이 누른 시도가 같은 꼴의 기록으로 하나씩 주어진다. 두 기록이 같다는 것은 이름이 같고, 번호의 개수가 같고, 자리마다 번호가 같다는 뜻이다. 번호는 수로 비교한다.
번호 앞에는 0이 붙어 있을 수 있어서, 007과 7은 적힌 모양이 달라도 같은 번호다. 번호가 같아도 차례가 다르면 다른 기록이고, 앞부분만 같고 개수가 다르면 다른 기록이다. 시도마다 한 줄씩 출력하라. 시도가 등록된 기록과 같으면 "열림"을 출력한다. 그렇지 않고 바로 앞 시도와 같으면 "반복"을 출력한다. 둘 다 아니면 "틀림"를 출력한다. 첫 시도에는 바로 앞 시도가 없다.

입력
1 ≤ n ≤ 1,000.
이름은 영어 소문자 1~10글자다.
1 ≤ k ≤ 10이고, 번호는 0 이상 1,000,000,000 이하인 정수다.
번호는 쉼표 없이 숫자 1~10글자로 적고, 앞에 0이 붙어 있을 수 있다(007은 7, 00은 0이다).
k와 n 앞에는 0이 붙지 않는다.
한 줄의 낱말 사이는 빈칸 하나다.
출력
n줄.
줄마다 "열림", "반복", "틀림" 가운데 하나
예시 1 · stdin
mina 3 7 300 12
6
mina 3 7 300 12
mina 2 7 300
mina 2 7 300
mino 3 7 300 12
mina 3 7 12 300
mina 3 7 300 12
stdout
열림
틀림
반복
틀림
틀림
열림
예시 2 · stdin
bike 2 1 23
6
bike 2 12 3
bike 1 123
bike 1 0123
bike 2 01 23
bike 2 1 023
bike 2 10 23
stdout
틀림
틀림
반복
열림
열림
틀림
main.py
입력(stdin)

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

$ 준비됐어요

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

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

'값이 같은가'와 '같은 물건인가'를 가르는 문제예요.
입력에서 따로 읽은 두 기록은 내용이 같아도 서로 다른 객체라서, 어느 쪽을 묻는 ==인지가 답을 바꿔요.
Python은 ==가 값 비교라 (이름, 번호 목록) == key가 길이와 자리마다의 값을 비교해 주고, 같은 객체인지는 is가 물어요.
C++도 std::string·std::vector의 ==가 값 비교라 구조체에 bool operator==(const Record&) const = default;(C++20) 한 줄이면 돼요.
Java는 ==가 객체에서는 같은 객체인지를 물어요.
문자열은 a.equals(b), 배열은 Arrays.equals(a, b)로 비교해요(List의 equals는 원소를 비교해요).
C#은 string의 ==가 내용을 비교하지만 배열은 ==도 Equals도 같은 배열인지만 물어서 a.SequenceEqual(b)를 써요.
JavaScript·TypeScript는 문자열의 ===가 내용 비교이고 배열의 ===는 같은 배열인지만 물어서, 길이를 비교하고 every로 자리마다 비교해요.
C는 배열 이름끼리의 ==가 주소 비교라 문자열은 strcmp(a, b) == 0, 번호는 반복문으로 비교해요(구조체끼리는 ==를 아예 쓸 수 없어요).
크레이콘은 문자열의 ==가 내용 비교이고 목록의 ==는 같은 목록인지를 물어서 자리마다 비교하는 함수를 만들어요.
함정 여섯: ① Java에서 이름을 ==로 비교하면 입력에서 읽은 문자열은 늘 다른 객체라 모두 "no"가 돼요.
② Java List<Integer>의 원소를 a.get(i) != b.get(i)로 비교하거나 Python에서 수를 is로 비교하면, 작은 수(Java -128~127, CPython -5~256)는 미리 만들어 둔 객체를 함께 써서 맞는 것처럼 보이다가 300 같은 큰 수에서 틀려요.
③ 배열을 join("")으로 붙여 비교하면 [1, 23]과 [12, 3]과 [123]이 모두 "123"이에요(예시 2).
붙이려면 join(",")처럼 사이 글자를 넣어요.
④ Python zip은 짧은 쪽에서 멈춰서, 길이를 먼저 비교하지 않으면 앞부분만 같은 기록도 같다고 나와요.
⑤ C에서 앞 시도를 복사하지 않고 주소(prev = &cur)만 기억하면, 같은 칸에 다음 시도를 읽는 순간 앞 시도도 바뀌어 자기 자신과 비교하게 돼요.
구조체 대입 prev = cur는 내용을 복사해요.
⑥ 줄이나 낱말을 문자열 그대로 비교하면 007과 7이 다르다고 나와요.
번호는 수로 바꾼 뒤에 비교해요(Python int, JS Number, Java Integer.parseInt, C# int.Parse, 크레이콘 수로변환).
C에서는 scanf의 %d로 읽어요.
%i는 0으로 시작하는 수를 8진수로 읽어서 010이 8이 돼요.

Pythonmain.py · 19 줄
def read_record(line):    """'이름 개수 번호…' 한 줄을 (이름, 번호 목록)으로."""    parts = line.split()    return parts[0], [int(x) for x in parts[2:]]  key = read_record(input())n = int(input())prev = None  # 첫 시도에는 앞 시도가 없다for _ in range(n):    attempt = read_record(input())    # 튜플·목록의 ==는 길이와 자리마다의 값을 비교한다(`is`는 같은 객체인지를 묻는다)    if attempt == key:        print("열림")    elif attempt == prev:        print("반복")    else:        print("틀림")    prev = attempt
JavaScriptmain.js · 25 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n"); // "이름 개수 번호…" 한 줄을 기록으로function readRecord(line) {  const parts = line.trim().split(" ");  return { name: parts[0], codes: parts.slice(2).map(Number) };} // 배열의 ===는 같은 배열인지만 물으므로, 길이와 자리마다의 값을 직접 비교한다function sameRecord(a, b) {  return a.name === b.name && a.codes.length === b.codes.length && a.codes.every((x, i) => x === b.codes[i]);} const key = readRecord(lines[0]);const n = Number(lines[1]);const out = [];let prev = null; // 첫 시도에는 앞 시도가 없다for (let i = 2; i < 2 + n; i++) {  const attempt = readRecord(lines[i]);  if (sameRecord(attempt, key)) out.push("열림");  else if (prev !== null && sameRecord(attempt, prev)) out.push("반복");  else out.push("틀림");  prev = attempt;}console.log(out.join("\n"));
TypeScriptmain.ts · 31 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n"); interface LockRecord {  name: string;  codes: number[];} // "이름 개수 번호…" 한 줄을 기록으로function readRecord(line: string): LockRecord {  const parts = line.trim().split(" ");  return { name: parts[0], codes: parts.slice(2).map(Number) };} // 배열의 ===는 같은 배열인지만 물으므로, 길이와 자리마다의 값을 직접 비교한다function sameRecord(a: LockRecord, b: LockRecord): boolean {  return a.name === b.name && a.codes.length === b.codes.length && a.codes.every((x, i) => x === b.codes[i]);} const key = readRecord(lines[0]);const n = Number(lines[1]);const out: string[] = [];let prev: LockRecord | null = null; // 첫 시도에는 앞 시도가 없다for (let i = 2; i < 2 + n; i++) {  const attempt = readRecord(lines[i]);  if (sameRecord(attempt, key)) out.push("열림");  else if (prev !== null && sameRecord(attempt, prev)) out.push("반복");  else out.push("틀림");  prev = attempt;}console.log(out.join("\n"));
JavaMain.java · 43 줄
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Arrays; public class Main {    static String name(String[] parts) {        return parts[0];    }     static int[] codes(String[] parts) {        int[] codes = new int[parts.length - 2];        for (int i = 0; i < codes.length; i++) codes[i] = Integer.parseInt(parts[i + 2]);        return codes;    }     // 문자열은 equals, 배열은 Arrays.equals로 내용을 비교한다(==는 같은 객체인지만 묻는다)    static boolean same(String nameA, int[] codesA, String nameB, int[] codesB) {        return nameA.equals(nameB) && Arrays.equals(codesA, codesB);    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] first = in.readLine().trim().split(" ");        String keyName = name(first);        int[] keyCodes = codes(first);        int n = Integer.parseInt(in.readLine().trim());        String prevName = null; // 첫 시도에는 앞 시도가 없다        int[] prevCodes = null;        StringBuilder out = new StringBuilder();        for (int i = 0; i < n; i++) {            String[] parts = in.readLine().trim().split(" ");            String curName = name(parts);            int[] curCodes = codes(parts);            if (same(curName, curCodes, keyName, keyCodes)) out.append("열림\n");            else if (prevName != null && same(curName, curCodes, prevName, prevCodes)) out.append("반복\n");            else out.append("틀림\n");            prevName = curName;            prevCodes = curCodes;        }        System.out.print(out);    }}
C#Main.cs · 35 줄
using System;using System.Linq;using System.Text; class Program{    static int[] Codes(string[] parts) => parts.Skip(2).Select(int.Parse).ToArray();     // string의 ==는 내용을 비교하지만, 배열의 ==는 같은 배열인지만 물어서 SequenceEqual을 쓴다    static bool Same(string nameA, int[] codesA, string nameB, int[] codesB) =>        nameA == nameB && codesA.SequenceEqual(codesB);     static void Main()    {        string[] first = Console.ReadLine().Trim().Split(' ');        string keyName = first[0];        int[] keyCodes = Codes(first);        int n = int.Parse(Console.ReadLine());        string prevName = null; // 첫 시도에는 앞 시도가 없다        int[] prevCodes = null;        var sb = new StringBuilder();        for (int i = 0; i < n; i++)        {            string[] parts = Console.ReadLine().Trim().Split(' ');            string curName = parts[0];            int[] curCodes = Codes(parts);            if (Same(curName, curCodes, keyName, keyCodes)) sb.Append("열림\n");            else if (prevName != null && Same(curName, curCodes, prevName, prevCodes)) sb.Append("반복\n");            else sb.Append("틀림\n");            prevName = curName;            prevCodes = curCodes;        }        Console.Write(sb);    }}
C++main.cpp · 35 줄
#include <iostream>#include <string>#include <vector> struct Record {    std::string name;    std::vector<int> codes;    // std::string과 std::vector의 ==는 내용을 비교하므로, 멤버끼리 비교하게만 하면 된다    bool operator==(const Record&) const = default;}; Record read_record() {    Record r;    int count;    std::cin >> r.name >> count;    r.codes.resize(count);    for (int& c : r.codes) std::cin >> c;    return r;} int main() {    Record key = read_record();    int n;    std::cin >> n;    Record prev;    bool has_prev = false;  // 첫 시도에는 앞 시도가 없다    for (int i = 0; i < n; i++) {        Record cur = read_record();        if (cur == key) std::cout << "열림\n";        else if (has_prev && cur == prev) std::cout << "반복\n";        else std::cout << "틀림\n";        prev = cur;        has_prev = true;    }}
Cmain.c · 41 줄
#include <stdbool.h>#include <stdio.h>#include <string.h> typedef struct {    char name[11];    int count;    int codes[10];} Record; /* 번호는 %d로 읽어 수로 둔다: 앞에 0이 붙은 007도 7이 된다 */static void read_record(Record *r) {    scanf("%10s %d", r->name, &r->count);    for (int i = 0; i < r->count; i++) scanf("%d", &r->codes[i]);} /* 배열 이름끼리의 ==는 주소를 비교한다. 문자열은 strcmp로, 번호는 자리마다 비교한다 */static bool same_record(const Record *a, const Record *b) {    if (strcmp(a->name, b->name) != 0) return false;    if (a->count != b->count) return false;    for (int i = 0; i < a->count; i++)        if (a->codes[i] != b->codes[i]) return false;    return true;} int main(void) {    Record key, prev, cur;    read_record(&key);    int n;    scanf("%d", &n);    bool has_prev = false; /* 첫 시도에는 앞 시도가 없다 */    for (int i = 0; i < n; i++) {        read_record(&cur);        if (same_record(&cur, &key)) puts("열림");        else if (has_prev && same_record(&cur, &prev)) puts("반복");        else puts("틀림");        prev = cur; /* 구조체 대입은 내용을 통째로 복사한다 */        has_prev = true;    }    return 0;}
Craykonmain.cray · 50 줄
// "이름 개수 번호…" 한 줄을 [이름, 번호, 번호, …] 목록으로 바꾼다.// 번호는 수로 바꿔 둔다: 문자열 그대로면 "007"과 "7"이 다르다기록읽기(줄){    낱말 = 줄.공백제거().나누기(" ");    기록 = [낱말[0]];    반복 (i = 2; i < 낱말.개수; i++)    {        기록.넣기(낱말[i].수로변환());    }    돌려주기 기록;} // 목록끼리의 ==는 같은 목록인지만 물으므로, 길이와 자리마다의 값을 직접 비교한다같은기록인가(a, b){    만약 (a.개수 != b.개수)    {        돌려주기 거짓;    }    반복 (i = 0; i < a.개수; i++)    {        만약 (a[i] != b[i])        {            돌려주기 거짓;        }    }    돌려주기 참;} 등록 = 기록읽기(콘솔.줄읽기());n = 콘솔.줄읽기().수로변환();앞시도 = 빈값; // 첫 시도에는 앞 시도가 없다반복 (k = 0; k < n; k++){    시도 = 기록읽기(콘솔.줄읽기());    만약 (같은기록인가(시도, 등록))    {        콘솔.쓰기("열림");    }    아니면 만약 (앞시도 != 빈값 && 같은기록인가(시도, 앞시도))    {        콘솔.쓰기("반복");    }    아니면    {        콘솔.쓰기("틀림");    }    앞시도 = 시도;}