{ }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()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

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

다른 언어로는?언어별 풀이 보기 · 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는 들어온 차례를 구조체에 넣어 비교의 마지막 기준으로 써요.
리눅스(glibc)의 qsort는 대개 차례를 지켜서 그냥 통과하기도 하지만, macOS에서는 실제로 섞여요.
약속이 아니니 기대면 안 돼요.
잇기는 Python " ".join(words), JS·TS words.join(" "), Java String.join(" ", words), C# string.Join(" ", words)예요.
C·C++는 첫 단어 앞에만 빈칸을 빼고 써요.
크레이콘은 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;}