{ }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()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 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,数字用循环比较(结构体之间根本不能用 ==)。
Craykon 中字符串的 == 比较内容,列表的 == 问的是是否同一个列表,所以要写一个逐个位置比较的函数。
六个陷阱:① 在 Java 中用 == 比较名字,从输入读到的字符串总是不同的对象,结果全是 "no"。
② 用 a.get(i) != b.get(i) 比较 Java List<Integer> 的元素,或在 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、Craykon 转数字)。
C 中用 scanf 的 %d 读取。
%i 会把以 0 开头的数按八进制读,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++){    시도 = 기록읽기(控制台.读行());    如果 (같은기록인가(시도, 등록))    {        控制台.输出("打开");    }    否则 如果 (앞시도 != 空 && 같은기록인가(시도, 앞시도))    {        控制台.输出("重复");    }    否则    {        控制台.输出("不对");    }    앞시도 = 시도;}