{ }codelper
大小写去除空白连接字符串P0145

带例外单词的标题格式

第一行给出例外单词个数 k,第二行给出用单个空格分隔的 k 个例外单词(k 为 0 时是空行)。
第三行给出标题个数 n,接着每行给出一个英文标题,共 n 个。
把每个标题改写成标题格式(title case),每行输出一个。
标题行按空格分成单词。
行首和行尾的空格丢掉,单词之间不管有几个空格都合并成一个。
如果某个单词末尾带有 :,它后面就是副标题。
: 也是这个单词的一部分,所以 "of:" 与例外单词 "of" 不相同。
对每个单词,先把所有英文大写字母变成小写。
如果这个单词与某个例外单词相同,并且它既不是第一个单词,也不是最后一个单词,也不是紧跟在以 : 结尾的单词后面的单词(副标题的第一个单词),就保持全部小写。
其他单词只把第一个字符变成大写。
如果第一个字符是数字,就保持不变。
' 或 - 后面的字符不是新单词的开始,所以保持小写("don't" → "Don't","e-mail" → "E-mail","3D" → "3d")。
把改好的单词用单个空格连接起来输出。

输入
0 ≤ k ≤ 16,例外单词互不相同,由 1 到 10 个英文小写字母组成。
1 ≤ n ≤ 100。
标题行长度 1 到 200,只由英文大小写字母、数字、'、-、: 和空格组成,至少有一个单词。
每个单词以英文字母或数字开头,: 只会出现在单词的最末尾
输出
改写后的 n 个标题,每行一个
样例 1 · stdin
5
a of the and in
4
  the lord OF the rings  
war   and peace
don't look 3d e-mail
the
stdout
The Lord of the Rings
War and Peace
Don't Look 3d E-mail
The
样例 2 · stdin
2
of the
3
OF THE
THE END OF THE WORLD
the hobbit: the desolation of smaug
stdout
Of The
The End of the World
The Hobbit: The Desolation of Smaug
main.py
输入(stdin)

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

$ 就绪

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

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

陷阱有五个。
第一,Python str.title() 把非字母后面紧跟的字符都当作新单词的开头,会得到 "Don'T"、"E-Mail"、"3D"。
每个单词只能改第一个字符,例如 low[0].upper() + low[1:](capitalize() 也是这样做的)。
大小写转换:JS·TS toUpperCase·toLowerCase,Java toUpperCase(Locale.ROOT),C# ToUpperInvariant·char.ToUpperInvariant,C·C++ 逐字 toupper·tolower,Craykon 大写()·小写()。
第二,要先变成小写再判断是不是例外单词,这样 "OF" 也能识别。
第三,不只是第一个单词,最后一个单词也不受例外规则影响:"OF THE" 是 "Of The"。
第四,: 后面副标题的第一个单词也不受例外规则影响:"the hobbit: the desolation" 里的第二个 "the" 是 "The"。
判断前一个单词是否以 : 结尾:Python endswith(":"),JS·TS·Java endsWith(":"),C# EndsWith(':'),C w[strlen(w) - 1] == ':',C++ w.back() == ':',Craykon 结尾是(":")。
第五,修剪空白和分割。
Python 不带参数的 split()、C# Split(' ', StringSplitOptions.RemoveEmptyEntries)、C strtok、C++ >>、Craykon 不带分隔符的 分割() 会跳过开头、末尾和中间所有的空格。
JS·TS·Java 的 split(" ") 会在两个空格之间和开头留下空字符串,所以要先 trim(),再用正则 / +/·" +" 分割。
连接用 Python " ".join(out),JS·TS out.join(" "),Java String.join(" ", words),C# string.Join(" ", words),Craykon out.连接(" ");C·C++ 除第一个单词外,每个单词前输出一个空格。

Pythonmain.py · 14 行
k = int(input())small = set(input().split())n = int(input())for _ in range(n):    words = input().split()  # 인자 없는 split()은 앞뒤·여러 칸 공백을 모두 없앤다    out = []    for i, w in enumerate(words):        low = w.lower()        # 첫 낱말, 마지막 낱말, : 뒤의 부제 첫 낱말은 예외 낱말이어도 대문자로 시작한다        if 0 < i < len(words) - 1 and not words[i - 1].endswith(":") and low in small:            out.append(low)        else:            out.append(low[0].upper() + low[1:])  # title()은 ' 와 - 뒤도 대문자로 만든다    print(" ".join(out))
JavaScriptmain.js · 17 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const k = Number(lines[0]);const small = new Set(lines[1].split(" ").filter((w) => w !== ""));const n = Number(lines[2]);const out = [];for (const line of lines.slice(3, 3 + n)) {  // trim 뒤에 공백 덩어리로 나눈다: split(" ")은 빈 칸마다 빈 문자열을 남긴다  const words = line.trim().split(/ +/);  const fixed = words.map((w, i) => {    const low = w.toLowerCase();    // 첫 낱말, 마지막 낱말, : 뒤의 부제 첫 낱말은 예외 낱말이어도 대문자로 시작한다    if (i > 0 && i < words.length - 1 && !words[i - 1].endsWith(":") && small.has(low)) return low;    return low[0].toUpperCase() + low.slice(1);  });  out.push(fixed.join(" "));}console.log(out.join("\n"));
TypeScriptmain.ts · 18 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const k: number = Number(lines[0]);const small = new Set<string>(lines[1].split(" ").filter((w) => w !== ""));const n: number = Number(lines[2]);const out: string[] = [];for (const line of lines.slice(3, 3 + n)) {  // trim 뒤에 공백 덩어리로 나눈다: split(" ")은 빈 칸마다 빈 문자열을 남긴다  const words: string[] = line.trim().split(/ +/);  const fixed: string[] = words.map((w: string, i: number): string => {    const low: string = w.toLowerCase();    // 첫 낱말, 마지막 낱말, : 뒤의 부제 첫 낱말은 예외 낱말이어도 대문자로 시작한다    if (i > 0 && i < words.length - 1 && !words[i - 1].endsWith(":") && small.has(low)) return low;    return low[0].toUpperCase() + low.slice(1);  });  out.push(fixed.join(" "));}console.log(out.join("\n"));
JavaMain.java · 30 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Arrays;import java.util.HashSet;import java.util.Locale;import java.util.Set; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        in.readLine();        Set<String> small = new HashSet<>(Arrays.asList(in.readLine().trim().split(" ")));        int n = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int t = 0; t < n; t++) {            // trim 뒤 정규식 " +"로 나눈다: split(" ")은 빈칸 두 개 사이에 빈 문자열을 남긴다            String[] words = in.readLine().trim().split(" +");            for (int i = 0; i < words.length; i++) {                String low = words[i].toLowerCase(Locale.ROOT);                // 첫 낱말, 마지막 낱말, : 뒤의 부제 첫 낱말은 예외 낱말이어도 대문자로 시작한다                boolean inner = i > 0 && i < words.length - 1 && !words[i - 1].endsWith(":");                if (inner && small.contains(low)) words[i] = low;                else words[i] = low.substring(0, 1).toUpperCase(Locale.ROOT) + low.substring(1);            }            out.append(String.join(" ", words)).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 26 行
using System;using System.Collections.Generic; class Program{    static void Main()    {        Console.ReadLine();        var small = new HashSet<string>(Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries));        int n = int.Parse(Console.ReadLine());        for (int t = 0; t < n; t++)        {            // RemoveEmptyEntries가 앞뒤·여러 칸 공백에서 생기는 빈 조각을 버린다            string[] words = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            for (int i = 0; i < words.Length; i++)            {                string low = words[i].ToLowerInvariant();                // 첫 낱말, 마지막 낱말, : 뒤의 부제 첫 낱말은 예외 낱말이어도 대문자로 시작한다                bool inner = i > 0 && i < words.Length - 1 && !words[i - 1].EndsWith(':');                if (inner && small.Contains(low)) words[i] = low;                else words[i] = char.ToUpperInvariant(low[0]) + low.Substring(1);            }            Console.WriteLine(string.Join(" ", words));        }    }}
C++main.cpp · 35 行
#include <cctype>#include <iostream>#include <sstream>#include <string>#include <unordered_set>#include <vector> int main() {    int k, n;    std::cin >> k;    std::unordered_set<std::string> small;    for (int i = 0; i < k; i++) {        std::string w;        std::cin >> w;        small.insert(w);    }    std::cin >> n;    std::string line;    std::getline(std::cin, line); // n 뒤의 줄 끝    for (int t = 0; t < n && std::getline(std::cin, line); t++) {        // >> 는 앞뒤·여러 칸 빈칸을 건너뛰고 낱말만 읽는다        std::istringstream ss(line);        std::vector<std::string> words;        for (std::string w; ss >> w;) words.push_back(w);        for (size_t i = 0; i < words.size(); i++) {            std::string& w = words[i];            for (char& c : w) c = (char)std::tolower((unsigned char)c);            // 첫 낱말, 마지막 낱말, : 뒤의 부제 첫 낱말은 예외 낱말이어도 대문자로 시작한다            bool inner = i > 0 && i + 1 < words.size() && words[i - 1].back() != ':';            if (!(inner && small.count(w))) w[0] = (char)std::toupper((unsigned char)w[0]);            std::cout << (i ? " " : "") << w;        }        std::cout << '\n';    }}
Cmain.c · 39 行
#include <stdio.h>#include <string.h>#include <ctype.h> static char small[20][16];static int k; static int isSmall(const char *w) {    for (int i = 0; i < k; i++)        if (strcmp(small[i], w) == 0) return 1;    return 0;} int main(void) {    int n;    char line[256];    scanf("%d", &k);    for (int i = 0; i < k; i++) scanf("%15s", small[i]);    scanf("%d", &n);    fgets(line, sizeof line, stdin); /* n 뒤의 줄 끝 */    for (int t = 0; t < n; t++) {        if (!fgets(line, sizeof line, stdin)) break;        line[strcspn(line, "\n")] = '\0';        /* strtok은 앞뒤·여러 칸 빈칸을 건너뛰고 낱말만 준다 */        char *words[128];        int m = 0;        for (char *w = strtok(line, " "); w; w = strtok(NULL, " ")) words[m++] = w;        for (int i = 0; i < m; i++) {            for (char *p = words[i]; *p; p++) *p = (char)tolower((unsigned char)*p);            /* 첫 낱말, 마지막 낱말, : 뒤의 부제 첫 낱말은 예외 낱말이어도 대문자로 시작한다 */            int inner = i > 0 && i < m - 1 && words[i - 1][strlen(words[i - 1]) - 1] != ':';            if (!(inner && isSmall(words[i])))                words[i][0] = (char)toupper((unsigned char)words[i][0]);            printf(i == 0 ? "%s" : " %s", words[i]);        }        putchar('\n');    }    return 0;}
Craykonmain.cray · 27 行
k = 控制台.读行().转数字();small = 字典();遍历 (w 在 控制台.读行().分割()){    small[w] = 真;}n = 控制台.读行().转数字();循环 (t = 0; t < n; t++){    // 기준 없는 나누기()는 앞뒤·여러 칸 빈칸을 모두 건너뛴다    words = 控制台.读行().去空白().分割();    out = [];    循环 (i = 0; i < words.数量; i++)    {        low = words[i].小写();        // 첫 낱말, 마지막 낱말, : 뒤의 부제 첫 낱말은 예외 낱말이어도 대문자로 시작한다        如果 (i > 0 && i < words.数量 - 1 && !words[i - 1].结尾是(":") && small.包含(low))        {            out.添加(low);        }        否则        {            out.添加(low[0].大写() + low.截取(1));        }    }    控制台.输出(out.连接(" "));}