25.09.26

하… 실버인데 바로못맞춤

import java.io.*;  
import java.util.*;  
class Main{  
    public static void main(String[] args) throws IOException {  
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));  
        Set<String> splitter = new HashSet<>();  
        int N = Integer.parseInt(br.readLine()); //문자 구분자 수  
        StringTokenizer st = new StringTokenizer(br.readLine());  
        for(int i=0 ; i<N ; i++){  
            splitter.add(st.nextToken());  
        }  
        int M = Integer.parseInt(br.readLine()); //숫자 구분자 수  
        st = new StringTokenizer(br.readLine());  
        for(int i=0 ; i<M ; i++){  
            splitter.add(st.nextToken());  
        }  
        int K = Integer.parseInt(br.readLine()); //병합지 수  
        st = new StringTokenizer(br.readLine());  
        for(int i=0 ; i<K ; i++){  
            splitter.remove(st.nextToken());  
        }  
        int S = Integer.parseInt(br.readLine()); //문자열 길이  
        String s = br.readLine();  
        for(String delimeter : splitter){  
            s = s.replaceAll(delimeter," ");  
        }  
        String[] results = s.split(" ");  
        StringBuilder sb2 = new StringBuilder();  
        for(String r : results){  
            if(r.equals("")) continue;  
            sb2.append(r+"\n");  
        }  
        System.out.print(sb2.toString());  
    }  
}

r.equals("")로 비교해야 하는 이유

이 질문이 코드의 핵심 동작을 이해하는 데 매우 중요합니다.

s.split(” “)는 문자열을 공백(” ”) 하나를 기준으로 자릅니다. 만약 원래 문자열에 구분자가 연속으로 나타나면 어떤 일이 벌어질까요?

예를 들어,

  • 구분자(splitter)에 , 와 . 가 있다고 가정해봅시다.

  • 입력 문자열 s가 “Hello,,world.123” 이었다고 해봅시다.

  1. replaceAll 실행 후:

    • s = s.replaceAll(”,”, ” ”)  s는 “Hello world.123” 이 됩니다. (쉼표 두 개가 공백 두 개로 바뀜)

    • s = s.replaceAll(”.”, ” ”)  s는 “Hello world 123” 이 됩니다.

  2. split(” ”) 실행:

    • “Hello world 123” 을 공백(” ”) 하나를 기준으로 자르면 결과는 String[] 배열이 됩니다.

    • [“Hello”, "", “world”, “123”]

    • 여기서 “Hello”와 “world” 사이의 연속된 두 개의 공백 때문에, 그 사이에는 아무 내용이 없는 빈 문자열 ""이 생성됩니다. split은 구분자 ‘사이’의 값을 반환하기 때문입니다.

정규표현식 쓰는 방법

  • 모든 구분자를 | (OR 연산자)로 연결하여 하나의 정규식 패턴을 만듭니다. 예: (,|;|.)
  • 이 패턴을 사용하여 replaceAll을 한 번만 호출합니다.
  • split 할 때, 공백 하나(” “)가 아닌 하나 이상의 공백(“\s+“)을 기준으로 자르면 빈 문자열("")이 생기는 문제를 해결할 수 있습니다.
import java.io.*;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

class MainImproved {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        Set<String> splitter = new HashSet<>();

        // 1. 구분자 입력받기 (이 부분은 동일)
        int N = Integer.parseInt(br.readLine());
        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i < N; i++) {
            splitter.add(st.nextToken());
        }
        int M = Integer.parseInt(br.readLine());
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < M; i++) {
            splitter.add(st.nextToken());
        }
        int K = Integer.parseInt(br.readLine());
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < K; i++) {
            splitter.remove(st.nextToken());
        }

        int S = Integer.parseInt(br.readLine());
        String s = br.readLine();

        // 2. [개선점 1] 모든 구분자를 하나의 정규식으로 만들기
        // Pattern.quote()는 '.', '*', '+' 같은 정규식 특수문자를 일반 문자로 처리해줌
        String regex = splitter.stream()
                               .map(Pattern::quote) // 특수문자 이스케이프 처리
                               .collect(Collectors.joining("|")); // "|"로 구분자들을 연결

        // 3. [개선점 2] replaceAll을 단 한 번만 호출
        // 구분자가 없는 경우 regex가 비어있을 수 있으므로 확인
        if (!splitter.isEmpty()) {
            s = s.replaceAll(regex, " ");
        }

        // 4. [개선점 3] "\\s+"를 사용하여 하나 이상의 공백으로 분리
        // 이렇게 하면 연속된 공백이 하나의 구분자처럼 취급되어 빈 문자열이 생기지 않음
        String[] results = s.trim().split("\\s+");

        // 5. 결과 출력 (이제 빈 문자열 체크가 필요 없음)
        StringBuilder sb = new StringBuilder();
        for (String r : results) {
            // s가 공백으로만 이루어진 경우 split 결과로 [""]가 나올 수 있으므로 한 번 더 체크
            if (!r.isEmpty()) { 
                sb.append(r).append("\n");
            }
        }
        System.out.print(sb.toString());
    }
}