{ }codelper
문자열 돌기대문자·소문자포함 검사P0269

한 손으로 치는 가장 긴 덩이

자판으로 영어를 칠 때 글자마다 치는 손이 정해져 있다. 왼손 글자는 q w e r t a s d f g z x c v b의 15개이고, 오른손 글자는 y u i o p h j k l n m의 11개이다. 대문자는 같은 소문자와 같은 손으로 친다. 영어 글자가 아닌 문자(숫자, 빈칸, 문장 부호)는 어느 손 글자도 아니다. 한 줄의 문자열이 주어진다. 문자열에서 영어 글자가 1개 이상 사이에 다른 문자 없이 바로 이어져 있고, 그 글자들이 모두 같은 손 글자인 부분을 한 손 덩이라고 한다. 세 줄을 출력하라. 첫째 줄에는 가장 긴 한 손 덩이의 길이를 출력한다. 둘째 줄에는 그 덩이를 입력에 나온 그대로(대소문자도 그대로) 출력한다.
가장 긴 덩이가 여럿이면 가장 왼쪽 것을 출력한다. 영어 글자가 하나도 없으면 첫째 줄에 0을, 둘째 줄에 "-"를 출력한다. 셋째 줄에는 바로 붙은 두 문자가 모두 영어 글자이면서 서로 다른 손 글자인 곳의 수를 출력한다. 두 영어 글자 사이에 다른 문자가 하나라도 끼어 있으면 그 둘은 바로 붙은 것이 아니다.

입력
한 줄, 길이 1 이상 100,000 이하.
문자는 빈칸(코드 32)부터 ~(코드 126)까지의 영어 자판 문자만 쓴다.
줄 앞뒤에 빈칸이 있을 수 있다.
출력
세 줄: 가장 긴 한 손 덩이의 길이, 그 덩이(없으면 -), 손이 바뀌는 곳의 수
예시 1 · stdin
Hello, minimum stewardess!
stdout
10
stewardess
2
예시 2 · stdin
Ab yo QAh
stdout
2
Ab
1
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

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

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

문자열을 한 글자씩 돌며 "바로 앞 글자의 손"과 "지금 글자에서 끝나는 덩이의 길이"만 들고 가면 돼요.
글자 하나를 꺼내는 법: Python·JS·TS·C#·크레이콘 s[i], Java s.charAt(i), C·C++ s[i](C는 char 하나, 다른 언어는 길이 1인 문자열이거나 char)예요.
대소문자를 무시하려면 Python ch.lower(), JS·TS toLowerCase(), Java Character.toLowerCase, C# char.ToLowerInvariant, C·C++ tolower, 크레이콘 .소문자로변환()으로 바꾼 다음 손 표에서 찾아요.
표에서 찾는 법도 달라요: Python c in LEFT, JS·TS LEFT.includes(c), Java·C# LEFT.indexOf(c) >= 0(C# IndexOf), C strchr, C++ find != npos, 크레이콘 LEFT.있나(c)예요.
C의 strchr는 끝의 '\0'까지 찾아서 strchr(LEFT, '\0')도 참이에요.
C 풀이는 isalpha로 영어 글자인지 먼저 묻고 나서 손 표를 찾아요.
첫째 함정은 줄 끝이에요.
덩이가 끊길 때만 가장 긴 것과 견주면 줄 끝까지 이어진 마지막 덩이를 놓쳐요.
글자마다 견주면 이 걱정이 없어요.
둘째로, 같은 길이면 왼쪽 것이니 >로 견줘요(>=면 오른쪽 것이 남아요).
셋째로, 손이 바뀌면 새 덩이는 그 글자부터라 길이가 0이 아니라 1이에요.
넷째로, 영어 글자가 아닌 것을 "왼손이 아니면 오른손"으로 두면 숫자·빈칸이 덩이에 들어가고, 손 바뀜을 "마지막으로 본 글자"와 견주면 빈칸 너머까지 세게 돼요.
입력 줄 앞뒤의 빈칸을 지우지 말고 그대로 읽어요(C fgets는 줄바꿈까지 담으니 그것만 지워요).

Pythonmain.py · 34 줄
LEFT = set("qwertasdfgzxcvb")RIGHT = set("yuiophjklnm") def hand(ch):    """영어 글자면 "L"·"R", 아니면 None. 대문자는 소문자로 바꿔 찾는다."""    c = ch.lower()    if c in LEFT:        return "L"    if c in RIGHT:        return "R"    return None s = input()best_len, best_start = 0, -1run = 0          # 지금 글자에서 끝나는 한 손 덩이의 길이switches = 0prev = None      # 바로 앞 글자의 손(글자가 아니면 None)for i, ch in enumerate(s):    h = hand(ch)    if h is None:        run = 0    elif h == prev:        run += 1    else:        if prev is not None:            switches += 1   # 바로 붙은 두 글자의 손이 다르다        run = 1             # 새 덩이는 이 글자부터    # 글자마다 견주면 줄 끝에서 끝나는 덩이도 놓치지 않는다. >라서 같으면 왼쪽 것이 남는다    if run > best_len:        best_len, best_start = run, i - run + 1    prev = hprint(best_len)print(s[best_start:best_start + best_len] if best_len else "-")print(switches)
JavaScriptmain.js · 21 줄
const s = require("fs").readFileSync(0, "utf8").split("\n")[0].replace(/\r$/, "");const LEFT = "qwertasdfgzxcvb", RIGHT = "yuiophjklnm";// 영어 글자면 "L"·"R", 아니면 null. 대문자는 소문자로 바꿔 찾는다// (includes는 부분 문자열을 찾으니 빈칸·기호 한 글자도 들어갈 수 없게 글자 하나만 넘긴다)const hand = (ch) => {  const c = ch.toLowerCase();  return LEFT.includes(c) ? "L" : RIGHT.includes(c) ? "R" : null;};let bestLen = 0, bestStart = -1, run = 0, switches = 0, prev = null;for (let i = 0; i < s.length; i++) {  const h = hand(s[i]);  if (h === null) run = 0;  else if (h === prev) run++;  else {    if (prev !== null) switches++;    run = 1;  }  if (run > bestLen) { bestLen = run; bestStart = i - run + 1; } // 같으면 왼쪽 것  prev = h;}console.log(`${bestLen}\n${bestLen ? s.slice(bestStart, bestStart + bestLen) : "-"}\n${switches}`);
TypeScriptmain.ts · 23 줄
import { readFileSync } from "node:fs";const s: string = readFileSync(0, "utf8").split("\n")[0].replace(/\r$/, "");const LEFT = "qwertasdfgzxcvb", RIGHT = "yuiophjklnm";type Hand = "L" | "R" | null;// 영어 글자면 "L"·"R", 아니면 null. 대문자는 소문자로 바꿔 찾는다const hand = (ch: string): Hand => {  const c = ch.toLowerCase();  return LEFT.includes(c) ? "L" : RIGHT.includes(c) ? "R" : null;};let bestLen = 0, bestStart = -1, run = 0, switches = 0;let prev: Hand = null;for (let i = 0; i < s.length; i++) {  const h: Hand = hand(s[i]);  if (h === null) run = 0;  else if (h === prev) run++;  else {    if (prev !== null) switches++;    run = 1;  }  if (run > bestLen) { bestLen = run; bestStart = i - run + 1; } // 같으면 왼쪽 것  prev = h;}console.log(`${bestLen}\n${bestLen ? s.slice(bestStart, bestStart + bestLen) : "-"}\n${switches}`);
JavaMain.java · 35 줄
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader; public class Main {    static final String LEFT = "qwertasdfgzxcvb", RIGHT = "yuiophjklnm";     // 영어 글자면 'L'·'R', 아니면 0. 대문자는 소문자로 바꿔 찾는다    static char hand(char ch) {        char c = Character.toLowerCase(ch);        if (LEFT.indexOf(c) >= 0) return 'L';        if (RIGHT.indexOf(c) >= 0) return 'R';        return 0;    }     public static void main(String[] args) throws IOException {        String s = new BufferedReader(new InputStreamReader(System.in)).readLine();        int bestLen = 0, bestStart = -1, run = 0, switches = 0;        char prev = 0;        for (int i = 0; i < s.length(); i++) {            char h = hand(s.charAt(i));            if (h == 0) run = 0;            else if (h == prev) run++;            else {                if (prev != 0) switches++;                run = 1;            }            if (run > bestLen) { bestLen = run; bestStart = i - run + 1; } // 같으면 왼쪽 것            prev = h;        }        System.out.println(bestLen);        System.out.println(bestLen > 0 ? s.substring(bestStart, bestStart + bestLen) : "-");        System.out.println(switches);    }}
C#Main.cs · 38 줄
using System; class Program{    const string Left = "qwertasdfgzxcvb", Right = "yuiophjklnm";     // 영어 글자면 'L'·'R', 아니면 '\0'. 대문자는 소문자로 바꿔 찾는다    static char Hand(char ch)    {        char c = char.ToLowerInvariant(ch);        if (Left.IndexOf(c) >= 0) return 'L';        if (Right.IndexOf(c) >= 0) return 'R';        return '\0';    }     static void Main()    {        string s = Console.ReadLine();        int bestLen = 0, bestStart = -1, run = 0, switches = 0;        char prev = '\0';        for (int i = 0; i < s.Length; i++)        {            char h = Hand(s[i]);            if (h == '\0') run = 0;            else if (h == prev) run++;            else            {                if (prev != '\0') switches++;                run = 1;            }            if (run > bestLen) { bestLen = run; bestStart = i - run + 1; } // 같으면 왼쪽 것            prev = h;        }        Console.WriteLine(bestLen);        Console.WriteLine(bestLen > 0 ? s.Substring(bestStart, bestLen) : "-");        Console.WriteLine(switches);    }}
C++main.cpp · 33 줄
#include <cctype>#include <iostream>#include <string> const std::string LEFT = "qwertasdfgzxcvb", RIGHT = "yuiophjklnm"; // 영어 글자면 'L'·'R', 아니면 0. 대문자는 소문자로 바꿔 찾는다char hand(char ch) {    char c = (char)std::tolower((unsigned char)ch);    if (LEFT.find(c) != std::string::npos) return 'L';    if (RIGHT.find(c) != std::string::npos) return 'R';    return 0;} int main() {    std::string s;    std::getline(std::cin, s);    if (!s.empty() && s.back() == '\r') s.pop_back();    int bestLen = 0, bestStart = -1, run = 0, switches = 0;    char prev = 0;    for (int i = 0; i < (int)s.size(); i++) {        char h = hand(s[i]);        if (h == 0) run = 0;        else if (h == prev) run++;        else {            if (prev != 0) switches++;            run = 1;        }        if (run > bestLen) { bestLen = run; bestStart = i - run + 1; } // 같으면 왼쪽 것        prev = h;    }    std::cout << bestLen << '\n' << (bestLen > 0 ? s.substr(bestStart, bestLen) : "-") << '\n' << switches << '\n';}
Cmain.c · 35 줄
#include <ctype.h>#include <stdio.h>#include <string.h> static char line[100005]; /* 영어 글자면 'L'·'R', 아니면 0. strchr는 끝의 '\0'도 찾으니 글자인지 먼저 본다 */static char hand(char ch) {    if (!isalpha((unsigned char)ch)) return 0;    char c = (char)tolower((unsigned char)ch);    return strchr("qwertasdfgzxcvb", c) ? 'L' : 'R';} int main(void) {    if (!fgets(line, sizeof line, stdin)) return 0;    line[strcspn(line, "\r\n")] = '\0'; /* fgets는 줄바꿈까지 담는다 */    int best_len = 0, best_start = -1, run = 0, switches = 0;    char prev = 0;    for (int i = 0; line[i]; i++) {        char h = hand(line[i]);        if (h == 0) run = 0;        else if (h == prev) run++;        else {            if (prev != 0) switches++;            run = 1;        }        if (run > best_len) { best_len = run; best_start = i - run + 1; } /* 같으면 왼쪽 것 */        prev = h;    }    printf("%d\n", best_len);    if (best_len > 0) printf("%.*s\n", best_len, line + best_start);    else puts("-");    printf("%d\n", switches);    return 0;}
Craykonmain.cray · 62 줄
LEFT = "qwertasdfgzxcvb";RIGHT = "yuiophjklnm"; // 영어 글자면 "L"·"R", 아니면 "". 대문자는 소문자로 바꿔 찾는다// (있나는 부분 문자열을 찾으니 늘 한 글자만 넘긴다)손(ch){    c = ch.소문자로변환();    만약 (LEFT.있나(c))    {        돌려주기 "L";    }    만약 (RIGHT.있나(c))    {        돌려주기 "R";    }    돌려주기 "";} s = 콘솔.줄읽기();bestLen = 0;bestStart = -1;run = 0; // 지금 글자에서 끝나는 한 손 덩이의 길이switches = 0;prev = ""; // 바로 앞 글자의 손("": 글자가 아님)반복 (i = 0; i < s.길이; i++){    h = 손(s[i]);    만약 (h == "")    {        run = 0;    }    아니면 만약 (h == prev)    {        run++;    }    아니면    {        만약 (prev != "")        {            switches++;        }        run = 1;    }    // 글자마다 견주면 줄 끝에서 끝나는 덩이도 놓치지 않는다. >라서 같으면 왼쪽 것    만약 (run > bestLen)    {        bestLen = run;        bestStart = i - run + 1;    }    prev = h;}콘솔.쓰기(bestLen);만약 (bestLen > 0){    콘솔.쓰기(s.부분문자열(bestStart, bestLen));}아니면{    콘솔.쓰기("-");}콘솔.쓰기(switches);