{ }codelper
比较字符串最大值/最小值P0386

词典页的书眉词

纸质词典在每一页的最上方印着这一页的第一个词和最后一个词。
这两个词叫做书眉词。
给出词典 p 页上的词,每一页内的顺序是打乱的。
词的先后按字符编码的顺序决定。
把两个词从前往后逐个字符比较,第一个不同的字符编码较小的词在前;如果一个词与另一个词的开头部分完全相同,则较短的词在前。
字符编码中连字符(-)最小,其次是大写字母 A~Z,再其次是小写字母 a~z。
先对每一页求出这一页中最靠前的词和最靠后的词,也就是书眉词。
然后给出 q 个要查的词,对每个词求出它应当出现在哪一页。
如果要查的词与第 i 页最靠前的词相同或在它之后,并且与第 i 页最靠后的词相同或在它之前,那么它应当出现的页就是第 i 页。

输入
第一行给出页数 p(1 ≤ p ≤ 100)。
接下来 p 行中的第 i 行给出第 i 页的词数 k 和 k 个词,用一个空格隔开(1 ≤ k ≤ 50)。
词是只由英文大写字母、小写字母和连字符(-)组成的字符串,长度为 1 到 15。
词典中所有的词互不相同。
第 i 页的每个词都在第 i+1 页的每个词之前。
下一行给出要查的词数 q(1 ≤ q ≤ 1,000)。
接下来 q 行每行给出一个要查的词。
要查的词也是由同样的字符组成、长度为 1 到 15 的字符串,可能是词典里没有的词。
输出
在前 p 行的第 i 行输出第 i 页最靠前的词和最靠后的词,用一个空格隔开。
只有一个词的页把这个词输出两次。
在接下来的 q 行,按给出的顺序每行输出一个要查的词应当出现的页码。
如果没有这样的页,输出 0。
样例 1 · stdin
3
3 Io IT Ice
4 cat apple banana app
1 zebra
6
apple
Io
app
ant
zebra
Ice
stdout
IT Io
app cat
zebra zebra
2
1
2
0
3
1
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

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

其他语言怎么写?查看各语言题解 · 8 种语言题解

比较字符串先后的方法,以及从多个中挑出最靠前的那个的方法,各语言并不相同。
Python 的 <、<= 和 min()、max() 可以直接用于字符串,按编码顺序比较。
还可以像 first <= w <= last 这样把比较连着写。
JavaScript 和 TypeScript 中字符串之间的 < 也是编码顺序。
但 Math.min("b", "a") 是 NaN(在 TypeScript 中是类型错误),所以最靠前的词要用循环自己找。
localeCompare 按区域顺序比较,"IT" 和 "Ice" 的先后与 < 相反。
Java 和 C# 对字符串用 < 是编译错误。
Java 看 a.compareTo(b) 的符号,Collections.min() 也用同样的顺序。
C# 中 string.CompareOrdinal(a, b) 是编码顺序,而 string.Compare、CompareTo 和 LINQ 的 Min()、Max() 是区域文化顺序。
C 看 strcmp(a, b) 的符号;C++ 对 std::string 直接用 <,还能用 minmax_element 一次求出两端。
Craykon 中字符串之间的 < 也是编码顺序。
陷阱一:连着写的比较。
在 JavaScript 中 first <= w <= last 不会报错,但前一个比较先变成 true 或 false,再拿这个值与词比较时,词被转成数而成为 NaN,结果是 false。
对示例 1 的六个要查的词全都输出 0。
TypeScript 用类型错误、Craykon 用编译错误拦住这个式子。
陷阱二:区域文化顺序。
示例 1 第 1 页的 Io IT Ice 按编码顺序是 IT、Ice、Io(大写 T 在小写 c 之前)。
C# 的 Min()、Max() 把顺序看成 Ice、Io、IT,于是输出的不是 IT Io 而是 Ice IT。
陷阱三:忽略大小写的比较。
用 Java 的 compareToIgnoreCase 比较,同一行也会得到 Ice IT。
陷阱四:与书眉词相同的词。
示例 1 的 Io 和 app 本身就是书眉词,如果用 < 而不是 <= 比较就会得到 0。
只有一个词的第 3 页的 zebra 也是这样。
陷阱五:每一页重新开始。
如果保存最靠前的词的变量没有在每一页重新开始,由于后面页的词都更靠后,第 1 页的那个词会一直留着。
示例 1 的第 2 页输出的不是 app cat 而是 IT cat。

Pythonmain.py · 26 行
import sys lines = sys.stdin.read().split("\n")p = int(lines[0])firsts = []lasts = []out = []for i in range(1, p + 1):    words = lines[i].split()[1:]    # 문자열 비교(<, min, max)는 글자 코드 순서다    first = min(words)    last = max(words)    firsts.append(first)    lasts.append(last)    out.append(first + " " + last) q = int(lines[p + 1])for j in range(q):    w = lines[p + 2 + j].strip()    page = 0    for i in range(p):        if firsts[i] <= w <= lasts[i]:  # 파이썬은 비교를 이어 쓸 수 있다            page = i + 1            break    out.append(str(page))print("\n".join(out))
JavaScriptmain.js · 32 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const p = Number(lines[0]);const firsts = [];const lasts = [];const out = [];for (let i = 1; i <= p; i++) {  const words = lines[i].trim().split(" ").slice(1);  // 문자열끼리 <는 코드 순서다(Math.min은 문자열에 쓸 수 없다)  let first = words[0];  let last = words[0];  for (const w of words) {    if (w < first) first = w;    if (w > last) last = w;  }  firsts.push(first);  lasts.push(last);  out.push(first + " " + last);} const q = Number(lines[p + 1]);for (let j = 0; j < q; j++) {  const w = lines[p + 2 + j].trim();  let page = 0;  for (let i = 0; i < p; i++) {    if (firsts[i] <= w && w <= lasts[i]) {      page = i + 1;      break;    }  }  out.push(String(page));}console.log(out.join("\n"));
TypeScriptmain.ts · 34 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const p: number = Number(lines[0]);const firsts: string[] = [];const lasts: string[] = [];const out: string[] = [];for (let i = 1; i <= p; i++) {  const words: string[] = lines[i].trim().split(" ").slice(1);  // 문자열끼리 <는 코드 순서다(Math.min은 문자열을 받지 않는다)  let first: string = words[0];  let last: string = words[0];  for (const w of words) {    if (w < first) first = w;    if (w > last) last = w;  }  firsts.push(first);  lasts.push(last);  out.push(first + " " + last);} const q: number = Number(lines[p + 1]);for (let j = 0; j < q; j++) {  const w: string = lines[p + 2 + j].trim();  let page: number = 0;  for (let i = 0; i < p; i++) {    if (firsts[i] <= w && w <= lasts[i]) {      page = i + 1;      break;    }  }  out.push(String(page));}console.log(out.join("\n"));
JavaMain.java · 40 行
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 p = Integer.parseInt(br.readLine().trim());        String[] firsts = new String[p];        String[] lasts = new String[p];        StringBuilder sb = new StringBuilder();        for (int i = 0; i < p; i++) {            String[] parts = br.readLine().trim().split(" ");            // 문자열에는 <가 없다: compareTo가 코드 순서로 견준다            String first = parts[1];            String last = parts[1];            for (int j = 2; j < parts.length; j++) {                if (parts[j].compareTo(first) < 0) first = parts[j];                if (parts[j].compareTo(last) > 0) last = parts[j];            }            firsts[i] = first;            lasts[i] = last;            sb.append(first).append(' ').append(last).append('\n');        }         int q = Integer.parseInt(br.readLine().trim());        for (int j = 0; j < q; j++) {            String w = br.readLine().trim();            int page = 0;            for (int i = 0; i < p; i++) {                if (firsts[i].compareTo(w) <= 0 && w.compareTo(lasts[i]) <= 0) {                    page = i + 1;                    break;                }            }            sb.append(page).append('\n');        }        System.out.print(sb);    }}
C#Main.cs · 45 行
using System;using System.Text; class Program{    static void Main()    {        int p = int.Parse(Console.ReadLine().Trim());        string[] firsts = new string[p];        string[] lasts = new string[p];        StringBuilder sb = new StringBuilder();        for (int i = 0; i < p; i++)        {            string[] parts = Console.ReadLine().Trim().Split(' ');            // CompareOrdinal이 코드 순서다(Compare, CompareTo, Min()은 문화권 순서)            string first = parts[1];            string last = parts[1];            for (int j = 2; j < parts.Length; j++)            {                if (string.CompareOrdinal(parts[j], first) < 0) first = parts[j];                if (string.CompareOrdinal(parts[j], last) > 0) last = parts[j];            }            firsts[i] = first;            lasts[i] = last;            sb.Append(first).Append(' ').Append(last).Append('\n');        }         int q = int.Parse(Console.ReadLine().Trim());        for (int j = 0; j < q; j++)        {            string w = Console.ReadLine().Trim();            int page = 0;            for (int i = 0; i < p; i++)            {                if (string.CompareOrdinal(firsts[i], w) <= 0 && string.CompareOrdinal(w, lasts[i]) <= 0)                {                    page = i + 1;                    break;                }            }            sb.Append(page).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 40 行
#include <algorithm>#include <iostream>#include <string>#include <vector>using namespace std; int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    int p;    cin >> p;    vector<string> firsts(p), lasts(p);    for (int i = 0; i < p; i++) {        int k;        cin >> k;        vector<string> words(k);        for (auto& w : words) cin >> w;        // std::string의 <는 코드 순서다        auto [lo, hi] = minmax_element(words.begin(), words.end());        firsts[i] = *lo;        lasts[i] = *hi;        cout << firsts[i] << ' ' << lasts[i] << '\n';    }     int q;    cin >> q;    for (int j = 0; j < q; j++) {        string w;        cin >> w;        int page = 0;        for (int i = 0; i < p; i++) {            if (firsts[i] <= w && w <= lasts[i]) {                page = i + 1;                break;            }        }        cout << page << '\n';    }    return 0;}
Cmain.c · 38 行
#include <stdio.h>#include <string.h> static char firsts[100][16];static char lasts[100][16]; int main(void) {    int p;    if (scanf("%d", &p) != 1) return 1;    for (int i = 0; i < p; i++) {        int k;        char w[16];        if (scanf("%d", &k) != 1) return 1;        for (int j = 0; j < k; j++) {            if (scanf("%15s", w) != 1) return 1;            /* strcmp는 부호만 믿는다: 음수면 앞, 양수면 뒤 */            if (j == 0 || strcmp(w, firsts[i]) < 0) strcpy(firsts[i], w);            if (j == 0 || strcmp(w, lasts[i]) > 0) strcpy(lasts[i], w);        }        printf("%s %s\n", firsts[i], lasts[i]);    }     int q;    if (scanf("%d", &q) != 1) return 1;    for (int j = 0; j < q; j++) {        char w[16];        if (scanf("%15s", w) != 1) return 1;        int page = 0;        for (int i = 0; i < p; i++) {            if (strcmp(firsts[i], w) <= 0 && strcmp(w, lasts[i]) <= 0) {                page = i + 1;                break;            }        }        printf("%d\n", page);    }    return 0;}
Craykonmain.cray · 41 行
쪽수 = 控制台.读行().转数字();첫낱말들 = [];끝낱말들 = [];循环 (i = 0; i < 쪽수; i++){    칸들 = 控制台.读行().去空白().分割();    // 문자열끼리 <는 코드 순서다    첫낱말 = 칸들[1];    끝낱말 = 칸들[1];    循环 (j = 2; j < 칸들.数量; j++)    {        如果 (칸들[j] < 첫낱말)        {            첫낱말 = 칸들[j];        }        如果 (칸들[j] > 끝낱말)        {            끝낱말 = 칸들[j];        }    }    첫낱말들.添加(첫낱말);    끝낱말들.添加(끝낱말);    控制台.输出(첫낱말 + " " + 끝낱말);} 찾을수 = 控制台.读行().转数字();循环 (j = 0; j < 찾을수; j++){    낱말 = 控制台.读行().去空白();    쪽 = 0;    循环 (i = 0; i < 쪽수; i++)    {        // 첫낱말들[i] <= 낱말 <= 끝낱말들[i]처럼 이어 쓰면 컴파일 오류다        如果 (첫낱말들[i] <= 낱말 && 낱말 <= 끝낱말들[i])        {            쪽 = i + 1;            跳出;        }    }    控制台.输出(쪽);}