{ }codelper
文字列の比較大文字・小文字つなげて出力P0103

大文字小文字を無視して並べる

一行目に単語の数 n が与えられ、二行目に n 個の単語が空白一つずつで区切られて与えられる。
単語を大文字小文字を無視した順に並べ替え、一行に空白一つずつ空けて出力せよ。
二つの単語の順は、両方の英大文字を小文字に変えてから文字コード(ASCII)で決める。
最初の文字から順に比べ、最初に違う文字のコードが小さいほうが前である。
一方の単語がもう一方の先頭部分と同じなら、短いほうが前である。
小文字にすると同じになる単語("Bob" と "bob")は、入力に出た順をそのまま保つ。
文字コードは数字 0〜9 が 48〜57、下線 _ が 95、小文字 a〜z が 97〜122 なので、"_x" は "alice" より前である。

入力
1 ≤ n ≤ 2000。
単語は 1〜20 文字で、英大文字・小文字、数字、下線(_)だけからなる
出力
並べ替えた n 個の単語を空白一つずつ空けて一行で
例 1 · stdin
6
bob Alice _x alice Bob x9
stdout
_x Alice alice bob Bob x9
例 2 · stdin
4
ab a AB a_
stdout
a a_ ab AB
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

落とし穴は三つです。
一つ目、そのまま並べ替えると大文字(65〜90)がすべて小文字(97〜122)より前なので、"Bob" が "alice" より前に来ます。
二つ目、大文字に変えて比べると下線 _(95)が文字の後ろに行き、小文字に変えたときと順が変わります。
Python key=str.upper、C# StringComparer.OrdinalIgnoreCase(ドキュメント上、大文字に変えたのと同じ)がそうです。
Java compareToIgnoreCase・CASE_INSENSITIVE_ORDER は最後に小文字で比べるので、この問題と合います。
JS localeCompare は文字コードではなく人の言語の規則なので _ の位置が違い、つづりが同じなら小文字を先に置くので入力順も崩れます。
三つ目、同じもの同士で入力順を保つには安定ソートが必要です。
Python sorted、JS・TS sort(ES2019 から)、Java List.sort、C# OrderBy は安定ソートです。
C# List.Sort・Array.Sort、C++ std::sort、C qsort は安定を約束しません。
C++ は std::stable_sort を使い、C は入った順を構造体に入れて比較の最後の基準にします。
Linux(glibc)の qsort はたいてい順を保つのでそのまま通ることもありますが、macOS では実際に混ざります。
約束ではないので頼ってはいけません。
つなぐのは Python " ".join(words)、JS・TS words.join(" ")、Java String.join(" ", words)、C# string.Join(" ", words) です。
C・C++ は最初の単語の前だけ空白を抜いて書きます。
Craykon は words.並べ替え(byLower) に -1・0・1 を返す関数を渡し(同じもの同士は順を保つ)、words.つなぐ(" ") でつなぎます。

Pythonmain.py · 4 行
n = int(input())words = input().split()# sorted는 안정 정렬이라, 소문자로 같은 단어끼리는 들어온 차례를 지킨다print(" ".join(sorted(words, key=str.lower)))
JavaScriptmain.js · 9 行
const [first, second] = require("fs").readFileSync(0, "utf8").split("\n");const words = second.trim().split(" ");const byLower = (a, b) => {  const x = a.toLowerCase();  const y = b.toLowerCase();  return x < y ? -1 : x > y ? 1 : 0; // < 는 글자 코드 순(localeCompare 아님)};// Array.prototype.sort는 ES2019부터 안정 정렬이다console.log(words.sort(byLower).join(" "));
TypeScriptmain.ts · 10 行
import { readFileSync } from "node:fs";const [, second] = readFileSync(0, "utf8").split("\n");const words: string[] = second.trim().split(" ");const byLower = (a: string, b: string): number => {  const x: string = a.toLowerCase();  const y: string = b.toLowerCase();  return x < y ? -1 : x > y ? 1 : 0; // < 는 글자 코드 순(localeCompare 아님)};// Array.prototype.sort는 ES2019부터 안정 정렬이다console.log(words.sort(byLower).join(" "));
JavaMain.java · 18 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Arrays;import java.util.Comparator;import java.util.List;import java.util.Locale; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        in.readLine();        List<String> words = Arrays.asList(in.readLine().trim().split(" "));        // List.sort(객체)는 안정 정렬(TimSort). 소문자로 바꾼 것을 글자 코드로 비교한다        words.sort(Comparator.comparing((String w) -> w.toLowerCase(Locale.ROOT)));        System.out.println(String.join(" ", words));    }}
C#Main.cs · 14 行
using System;using System.Linq; class Program{    static void Main()    {        Console.ReadLine();        string[] words = Console.ReadLine().Trim().Split(' ');        // OrderBy는 안정 정렬. Ordinal은 글자 코드 순(문화권 규칙 없이)        var sorted = words.OrderBy(w => w.ToLowerInvariant(), StringComparer.Ordinal);        Console.WriteLine(string.Join(" ", sorted));    }}
C++main.cpp · 22 行
#include <algorithm>#include <cctype>#include <iostream>#include <string>#include <vector> static std::string lower(std::string s) {    for (char& c : s) c = (char)std::tolower((unsigned char)c);    return s;} int main() {    int n;    std::cin >> n;    std::vector<std::string> words(n);    for (auto& w : words) std::cin >> w;    // std::sort는 안정 정렬이 아니다: 같은 것끼리 차례를 지키려면 stable_sort    std::stable_sort(words.begin(), words.end(),                     [](const std::string& a, const std::string& b) { return lower(a) < lower(b); });    for (int i = 0; i < n; i++) std::cout << (i ? " " : "") << words[i];    std::cout << '\n';}
Cmain.c · 31 行
#include <ctype.h>#include <stdio.h>#include <stdlib.h> struct Word {    char text[21];    int order; /* 들어온 차례: qsort는 안정 정렬이 아니라서 직접 비교에 넣는다 */}; static int by_lower(const void *pa, const void *pb) {    const struct Word *a = pa, *b = pb;    const unsigned char *x = (const unsigned char *)a->text, *y = (const unsigned char *)b->text;    while (*x && tolower(*x) == tolower(*y)) x++, y++;    int d = tolower(*x) - tolower(*y); /* 한쪽이 끝나면 '\0'(0)이라 짧은 쪽이 앞 */    if (d != 0) return d;    return a->order - b->order;} int main(void) {    static struct Word w[2000];    int n;    scanf("%d", &n);    for (int i = 0; i < n; i++) {        scanf("%20s", w[i].text);        w[i].order = i;    }    qsort(w, n, sizeof w[0], by_lower);    for (int i = 0; i < n; i++) printf(i ? " %s" : "%s", w[i].text);    printf("\n");    return 0;}
Craykonmain.cray · 21 行
n = コンソール.行読み().数に();words = コンソール.行読み().分ける();// 정렬(기준함수)는 같은 것끼리 들어온 차례를 지킨다words.並べ替え(byLower);コンソール.書く(words.つなぐ(" ")); // 소문자로 바꾼 것을 글자 코드 순으로 비교byLower(앞, 뒤){    a = 앞.小文字();    b = 뒤.小文字();    もし (a < b)    {        返す -1;    }    もし (a > b)    {        返す 1;    }    返す 0;}