[알고리즘 첫 단계] 문자열 분석 10820번 문제 Character 클래스와 나의 문제점 KPT자료 구조/알고리즘2025. 8. 5. 21:58
반응형
1. 내 코드
내가 코드를 짠 것
import java.util.Scanner
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
sc.nextLine();
StringBuilder sb = new StringBuilder(str);
for(int i=0; i<str.length(); i++){
// 소문자, 대문자, 숫자, 공백을 어떻게 구현 낼 것이냐..?를 잘 모르겠네..흠..
}
}
}
어제 나온 StringBuilder를 생각해낸 것까지는 잘 했는데 이후 소문자, 대문자, 숫자, 공백을 어떻게 구현 낼 것인가에 대해서 막힘
📝 K · P · T 정리
| Keep | 1) 입력 → 한 줄씩 처리하려는 구조를 이미 떠올렸다.2) 반복문에서 문자를 하나씩 접근해 분류해야 한다는 감을 잡았다. |
| Problem | 1) StringBuilder는 필요 없는 단계 - 지금은 단순 계수(count) 문제.2) 문제 원문(백준 10820)은 N이 주어지지 않고 EOF까지 읽는다.3) 문자 분류 방법(소문자/대문자/숫자/공백)- Character 헬퍼 메서드를 활용하면 깔끔하다. |
| Try | ① 입력 루프 → while (sc.hasNextLine()) 또는 BufferedReader.readLine().② 한 줄마다 카운터 4개 초기화 → 문자마다 if-else로 분류.③ 출력 형식 lower upper digit space 순으로 System.out.println. |
1️⃣ 사고 흐름 잡기
🔍 접근 방법
이 문제는 문자열 순회 및 문자 유형별 카운팅을 활용하는 문제입니다.
핵심 포인트:
• 문자열에 포함된 각 문자를 한 글자씩 검사
• 문자 유형에 따라 카운트 (소문자, 대문자, 숫자, 공백)
• 문자열 길이와 개수가 작기 때문에 단순 순회로도 충분 접근 방법:
1. 입력받은 문자열을 한 글자씩 순회
2. Character 메서드 (isLowerCase, isUpperCase, isDigit, 공백 검사)를 활용하여 카운트
3. 문자열마다 결과를 출력
주의사항:
- 문자열마다 결과를 따로 출력해야 함
- 입력에 다른 문자는 포함되지 않으므로 별도 예외 처리 불필요
- 입력 방식 결정
- _EOF(End Of File)_까지 읽는 문제 → while (sc.hasNextLine()) 패턴.
- 줄 수가 적고 길이 ≤ 100이므로 Scanner도 OK. (더 빠른 BufferedReader도 옵션)
- 4 개 카운터 초기화
- int lower = 0, upper = 0, digit = 0, space = 0;
- 문자 하나씩 분류
- Character.isLowerCase(c) → lower++
- Character.isUpperCase(c) → upper++
- Character.isDigit(c) → digit++
- c == ' ' → space++
- 라인마다 결과 출력
- System.out.println(lower + " " + upper + " " + digit + " " + space);
2️⃣ 완성 코드 (Scanner 버전)
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// EOF까지 한 줄씩 읽는다
while (sc.hasNextLine()) {
String line = sc.nextLine();
int lower = 0, upper = 0, digit = 0, space = 0;
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if (Character.isLowerCase(c)) {
lower++;
} else if (Character.isUpperCase(c)) {
upper++;
} else if (Character.isDigit(c)) {
digit++;
} else if (c == ' ') {
space++;
}
}
System.out.println(lower + " " + upper + " " + digit + " " + space);
}
}
}
BufferedReader + StringTokenizer 버전 (빠른 입출력 필요할 때)
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = br.readLine()) != null) { // EOF까지
int lower = 0, upper = 0, digit = 0, space = 0;
for (char c : line.toCharArray()) {
if (c >= 'a' && c <= 'z') lower++;
else if (c >= 'A' && c <= 'Z') upper++;
else if (c >= '0' && c <= '9') digit++;
else if (c == ' ') space++;
}
System.out.println(lower + " " + upper + " " + digit + " " + space);
}
}
}
💻 풀이 코드 3
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String input;
while ((input = br.readLine()) != null) {
int lower = 0, upper = 0, digit = 0, space = 0;
for (char ch : input.toCharArray()) {
if (Character.isLowerCase(ch)) lower++;
else if (Character.isUpperCase(ch)) upper++;
else if (Character.isDigit(ch)) digit++;
else if (ch == ' ') space++;
}
System.out.printf("%d %d %d %d%n", lower, upper, digit, space);
}
}
}
📝 코드 설명
- BufferReader로 입력을 받아 한 줄 씩 처리
- 각 문자를 for-each 로 순회하면서 Character class 의 method 를 사용하여 유형 판별
- 유형별로 카운트 한 후 print 로 출력
- 입력이 끝날 때 까지 반복
📊 복잡도 분석
시간 복잡도: O(N * L) (N = 문자열 개수, L = 문자열 길이)
공간 복잡도: O(1) (카운트 용 정수 변수 4개만 사용)
⚡ 최적화 팁
1. [Character 클래스 활용] : 자바의 Character 메서드는 내부적으로 빠르게 동작함
2. [toCharArray()로 변환 후 순회] : 문자열 길이가 작을 경우 메모리 부담이 없어 효율
🎯 학습 포인트
- 문자열을 순회하여 유형별로 카운트하는 기본 패턴 - 표준 출력 포맷팅 (printf)의 활용
🔗 관련 문제
- 문자열 분석 문제는 항상 문자별로 직접 순회하는 게 안전
- Java에서는 Character 클래스 메서드를 습관적으로 활용하면 좋음
- 출력이 많을 경우는 BufferedWriter도 고려
3️⃣ 학습 포인트: 키워드 기억할 것
| EOF 입력 패턴 | while (scanner.hasNextLine()) / while ((line = br.readLine()) != null) |
| 문자 분류 | Character.isLowerCase, isUpperCase, isDigit + 직접 비교 c == ' ' |
| 불변(immutable) String | 카운팅만 할 땐 StringBuilder 불필요 — 오히려 더 느려질 수 있음 |
| 성능 차이 | BufferedReader > Scanner (정규식 파싱 비용) — 큰 데이터 땐 유의 |
🚀 한 걸음 더
- 승급 문제: 문자 종류가 더 늘어날 때(특수문자 등) → switch(c) 활용.
- Stream API 연습: line.chars().forEach(...)로도 구현해 볼 수 있다.
2. Character 클래스 한눈에 이해하기
| 정체 | 자바 기본 타입 char의 래퍼(wrapper) 클래스 → int ↔ Integer, double ↔ Double와 같은 맥락 |
| 필요한 이유 | 1) 객체로 다뤄야 하는 상황(제네릭, 컬렉션)에 char를 넣고 싶을 때 2) 문자를 판별·변환하는 유틸리티 메서드를 모아 놓음 |
| 주요 기능 | - isLowerCase(c), isUpperCase(c), isDigit(c) … → 문자 분류- toLowerCase(c), toUpperCase(c) → 대소문자 변환- getType(c) → 유니코드 카테고리 반환 |
| 사용 예 | java <br>char c = 'A';<br>if (Character.isUpperCase(c)) { ... }<br> |
| 오토박싱 | Character ch = 'a'; 처럼 기본형 ↔ 객체 자동 변환 가능 (JDK 1.5+) |
왜 ‘static 메서드’가 많을까?
Character 객체를 굳이 만들 필요 없이 문자 하나(char)만 넘겨주면 바로 판별/변환하도록 설계됐기 때문입니다.
char x = '9';
boolean isNum = Character.isDigit(x); // true
char lower = Character.toLowerCase('G'); // 'g'
실무 팁
- 문자 판별이 필요하면 Character 메서드를 먼저 떠올리기.
- 컬렉션에 문자 넣기 → List<Character> 처럼 제네릭 타입을 써야 할 때 사용.
- 성능상 거의 부담 없음(메서드가 단순 분기), 가독성이 확 좋아진다.
한 줄 요약 : Character = char 전용 도우미 + 래퍼 객체.
문자와 관련된 “이건 소문자야?”, “숫자야?” 같은 질문이 생기면 바로 Character를 호출하세요!
3. BufferedReader.readLine() 과 throws IOException
| Keep | 1) BufferedReader.readLine() 같은 I/O 메서드는 IOException을 발생시킬 수 있다는 것을 인지했다. 2) 문자를 분류할 때 직접 범위 비교(a ~ z, A ~ Z, 0 ~ 9)와 Character 헬퍼 메서드 두 가지 방식을 모두 시도해 보았다. |
| Problem | 1) throws IOException 의 의미와 필요성이 모호했다. 2) 두 분류 방식의 차이·장단점이 헷갈린다. |
| Try | ▸ throws IOException → “예외가 발생하면 이 메서드 바깥으로 전파할 테니 호출자가 책임져라”는 선언. ▸ 문자 분류 두 방식의 차이: 표현 범위·가독성·확장성 비교. |
1️⃣ throws IOException — 왜 붙일까?
| 체크 예외(Checked Exception) | IOException은 컴파일러가 처리(선언/캐치) 강제하는 예외. |
| 발생 원인 | 스트림이 닫힘, 디코딩 문제, 하드웨어 오류 등 I/O 도중 예기치 못한 상황. |
| 선언 방식 | public static void main(String[] args) **throws IOException** { … }→ “try-catch를 직접 쓰지 않을 테니, JVM(혹은 호출자)이 대신 처리하라” |
| 장점 | • 테스트·실행 코드 분리• 예외 발생 시 프로그램이 즉시 종료되어 원인 파악이 쉬움 |
| 대안 | try-with-resources / try-catch 블록으로 예외를 직접 처리할 수도 있다. |
2️⃣ 두 문자 분류 방식 비교
if (c >= 'a' && c <= 'z') … Character 메서드if (Character.isLowerCase(c)) …
| 표현 범위 | ASCII(영문자·숫자)만 확실 – 다른 유니코드 범위는 직접 추가해야 함. | Character는 유니코드 전범위 지원 → 한글·그리스 문자 등도 올바르게 판별. |
| 가독성 | 범위가 눈에 보이므로 즉시 이해 가능.단, 조건식이 길어지고 실수 위험 ↑ | 메서드명 자체가 의미를 담고 있어 의도 파악이 쉽다. |
| 확장성 | 새로운 문자군 추가 시 직접 if-else 수정해야 함. | Character API에 이미 isLetter, isWhitespace, getType 등 다수 존재. |
| 성능 | 단순 비교 2회 – JIT 최적화로 매우 빠름. | 내부적으로도 유사한 분기 + 테이블 참조 → 차이 미미(나노초 수준). |
| 의존성 | java.lang 기본문자 범위만 사용 → 가볍다. | 같은 java.lang 패키지이므로 추가 import 필요 없음. |
| 추천 상황 | • Online Judge에서 ASCII만 주어진다고 명시.• 속도 미세 최적화가 중요한 경우. | • 다국어/유니코드 대응 필요.• 가독성과 유지보수 우선. |
실무 팁: 백준 같은 문제(입력 조건이 ‘알파벳·숫자·공백만’)이라면 두 방식 모두 OK.
다만 팀 프로젝트처럼 언어 범위가 확장될 가능성이 있으면 Character가 훨씬 안전합니다.
🚀 핵심 한 줄 요약
- throws IOException : “I/O 중 문제가 생기면 이 메서드 밖으로 예외를 던질 테니 상위 호출자가 처리해라”는 선언.
- 문자 분류 : 직접 범위 비교는 단순·빠름(ASCII 한정), Character 메서드는 가독성·유니코드 대응에 강점.
반응형
'자료 구조 > 알고리즘' 카테고리의 다른 글
| [알고리즘의 첫 단계] 백준 1032번과 Scanner와 StringBuilder (6) | 2025.08.04 |
|---|---|
| 연결리스트 (0) | 2024.11.22 |
| 구현 알고리즘 (1) | 2024.11.14 |
| 그리디 알고리즘이란? (2) | 2024.11.08 |
@mellona :: 주니어의 다사다난 성장기
안녕하세요. si 회사 소속 sm LMS 팀에 소속중인 2년차 백엔드 개발자입니다😀 함께 나누고 성장하는 것을 좋아해요. 언제든 디스코드나 구글 메일로 질문해도 됩니다!
⭐ 잘못된 내용은 댓글 적어주세요 :)