본문 바로가기

PS

백준 문제를 Java로 제출하기

백준 문제를 자바로 제출하기 위해 몇 가지 최적화하는 방법을 소개하고자 한다.

 

클래스 이름은 Main 이며 Package는 없어야한다.

 

import java.io.*;

class classA {
...
}

class classB {
...
}

public class Main {
...
}

 

위 코드처럼 백준에서 제출 시 최상단에 있던 Package를 지워주고, class 이름은 Main으로 제출해야한다. 또한 Main 클래스의 접근지정자는 유일무이한 public이어야 한다. 다른 클래스에 public이 존재해서는 안된다.

 

입력은 Scanner 대신 BufferedReader로 입력받는다.

 

흔히 Scanner scanner = new Scanner(System.in); 으로 입력을 받는다. 그러나 Scanner와 BufferedReader의 버퍼 크기를 놓고 비교하자면 BufferedReader의 버퍼 크기가 훨씬 크기 때문에 입력을 받는 속도 면에서 시간을 단축할 수 있다. 간단한 입력은 상관없지만 많은 문제를 접하다보면 입력이 복잡한 경우도 종종 있다. 따라서 Scanner보다는 BufferedReader를 통해 입력을 받는 습관을 들이도록 하자. 이 글에서 자바뿐만 아니라 C, Python에서의 입력 속도를 비교한 내용을 확인할 수 있다.

 

자바에서 입력을 받는 방법을 예제 몇 가지를 통해 알아보겠다. 먼저 공통적으로 BufferedReader 객체를 선언해야 한다. BufferedReader와 InputStreamReader 모두 io 패키지에 존재한다.

 

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
	public static void main(String[] args) throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	}
}

1. 문자열 하나 또는 정수 하나만 입력받을 때

Primitive

 

입력이 다음과 같다고 하자. String 객체 str에 "Primitive"를 저장하는 방법은 다음과 같다.

 

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
	public static void main(String[] args) throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            	String str = br.readLine();
	}
}

 

BufferedReader의 readLine() 메소드를 통해 str에 문자열을 저장할 수 있다. readLine()으로 입력받으면 문자열로 저장된다. 

6

 

다음 예제와 같이 입력받을 자료형이 정수형이라면 어떻게 해야할까? 문자열을 정수로 바꿔주는 Integer.parseInt() 메소드를 이용하여 입력받을 수 있다.

 

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
	public static void main(String[] args) throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            	int N = Integer.parseInt(br.readLine());
	}
}

2. 공백으로 구분된 문자열 또는 정수 여러 개를 입력받을 때

A B C

 

다음 예제와 같이 한 줄에 문자 여러 개를 입력받고자 하면 StringTokenizer를 이용하여 저장할 수 있다. 코드는 다음과 같다.

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	public static void main(String[] args) throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		String A = st.nextToken();
		String B = st.nextToken();
		String C = st.nextToken();
	}
}

 

StringTokenizer의 경우에는 자바의 util 패키지에 존재한다. 위 코드에서 String A에는 문자 A를, String B에는 문자 B를, String C에는 문자 C를 저장한다. StringTokenizer 객체 st를 선언할 때 매개변수로 br.readLine()만 존재하고 구분자는 따로 명시하지 않았기 때문에 StringTokenizer가 문자열을 공백을 기준으로 하여 토큰으로 분리한다. StringTokenizer의 nextToken() 메소드는 구분한 토큰들을 반환한다.

8 3

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	public static void main(String[] args) throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		int N = Integer.parseInt(st.nextToken());
		int M = Integer.parseInt(st.nextToken());
	}
}

 

정수를 공백으로 입력받고자 할 때는 앞서 설명했던 Integer.parseInt() 메소드를 사용하면 된다.

3 5
0 1 2 3 4

 

다음 입력 예제에서 첫째 줄에는 임의의 정수 N, M을 입력받고 둘째 줄에는 M개의 정수를 입력받고자 한다.  위 경우에는 StringTokenizer와 반복문을 이용하여 입력받을 수 있다. 주의할 점은 StringTokenizer를 2번 선언해줘야 한다. 

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	public static void main(String[] args) throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		int N = Integer.parseInt(st.nextToken());
		int M = Integer.parseInt(st.nextToken());
		
		int[] data = new int[M];
		st = new StringTokenizer(br.readLine()); // st 재선언
		
		for (int i = 0; i < M; i++) {
			data[i]= Integer.parseInt(st.nextToken());
		}	
	}
}

3. 문자열을 입력받고 각각을 하나의 문자로 분리하고 싶을 때

1234567890

 

만약 각각의 숫자 하나하나를 다뤄야하는 상황에서 위 예제와 같이 입력받으면 어떻게 해야할까? 자바에는 문자열을 문자로 변환해주는 toCharArray() 메소드가 있다. 이를 이용하면 간단하게 나눌 수 있다. toCharArray() 메소드는 String 클래스에 존재하므로 입력도 문자열로 입력받도록 한다.

 

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
	public static void main(String[] args) throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String N = br.readLine();
		
		for(char c : N.toCharArray()) {
			...
		}
	}
}

 

데이터를 입력받고 저장하는 것은 문제를 푸는 것에서 가장 처음이고 기초적이므로 중요하다.

 

출력은 System.out.println()을 남발하는 대신 StringBuilder를 이용하자

 

입력에서 속도로 인하여 Scanner 대신 BufferedReader와 StringTokenizer를 이용한 바와 마찬가지로, 결과를 출력하고자 할 때 단순히 System.out.println()를 사용하는 것 대신에 StringBuilder를 이용하면 속도 면에서 더 이득을 볼 수 있다. StringBuilder는 lang 패키지에 존재하므로 별도의 import문이 필요가 없다. 입력과 코드는 다음과 같다.

3 5
0 1 2 3 4

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	public static void main(String[] args) throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		StringBuilder sb = new StringBuilder();
		int N = Integer.parseInt(st.nextToken());
		int M = Integer.parseInt(st.nextToken());
		
		int[] data = new int[M];
		st = new StringTokenizer(br.readLine()); // st 재선언
		
		for (int i = 0; i < M; i++) {
			data[i]= Integer.parseInt(st.nextToken());
			sb.append(data[i]).append(" ");
		}	
		System.out.println(sb);
	}
}

 

출력 결과는 다음과 같다.

0 1 2 3 4

 

위의 코드에서 append() 메서드를 사용하여 data[i] 값을 StringBuilder 객체 sb에 삽입하고 공백도 함께 추가한다. 따라서 해당 코드는 data 배열의 요소들이 공백으로 구분된 문자열 형태로 sb에 삽입된다. 결과값을 하나의 객체 sb에 모아두고 나서 System.out.println(sb)로 한번에 출력하는 방법이 코드가 보기에도 깔끔하고 System.out.println()을 여러 번 사용하지 않아도 된다.

 

출력 또한 입력과 마찬가지로 알고리즘 문제의 정답을 보여주는 영역이므로 이 또한 기초적이고 중요하다.

 

문제를 풀면서 추가하거나 수정해야 할 부분이 있으면 계속 수정해야겠다.