[백준] 2941번 : 크로아티아 알파벳 - JAVA [자바]
https://www.acmicpc.net/problem/2941
-
문제
※ 주의할 점
- 크로아티아 알파벳의 개수를 세어야 한다.
- 배열로 풀 때 참조하려는 인덱스(index) 가 벗어나지 않는지 유의해야 한다.
- 8개의 문자는 특정 조건에 의해 변경되어 하나의 문자를 이루게 된다.
- 2가지 입력방법을 이용하여 풀이한다.
Scanner 로 입력받아 연산하는 방법과 BufferedReader 로 입력받아 연산하는 방법, 두 가지 방법을 통해 풀이해보고자 한다.
- 알고리즘
유의할 점은 알고리즘 설명에서는 의사코드(pseudocode)로 작성한다.
그래서 알고리즘에서 설명하는 코드를 백날 컴파일해봤자 에러만 뱉어낼것이다.
의사코드로 설명하는 이유는 행여나 자바가 아닌 다른 언어를 사용하는 경우에도 쉽게 자신이 쓰는 언어로 변경시킬 수 있도록 하기 위함이다.
의사코드로 작성하되, 기본 문법은 자바에 의거한다.
( charAt() 메소드를 쓰게 되는데 이 메소드는 문자열을 배열로 보고 해당 위치의 문자를 반환하는 메소드다. 시작값은 0 이다. )
1. 먼저 최대 100개의 글자로 이루어진 문자열 str 이 주어진다.
그리고 문자의 개수를 셀 변수 count 를 만들고 문자열에 대하여 문자열 길이만큼 반복할 반복문을 구성한다.
String str = input();
int count = 0;
for ( int i = 0; i < str.length; i++ ) { }
2. 이제 조건문을 작성해야한다.
예로들면 dz= 가 입력되면 dž 로 하나의 단어로 셀 수 있도록 해야하기 때문이다.
일단, 아래 변경될 문자 표를 다시 한 번 보자.
만약 참조하려는 문자가 c 를 입력받는다면 그 다음 문자를 참조하여 = 일 경우 č , - 일경우 ć 로 하나의 문자로 보는 것이다.
일단 c= 와 c- 가 있는 경우만 작성해보자면 아래와 같다.
String str = input();
int count = 0;
for (int i = 0; i < str.length; i++) {
char ch = str.charAt(i);
if(ch == 'c') { // 만약 ch 가 c 라면?
if(str.charAt(i + 1) == '=') { //만약 ch 다음 문자가 '=' 이라면?
// i+1 까지가 하나의 문자이므로 다음 문자를 건너 뛰기 위해 1 증가
i++;
}
else if(str.charAt(i + 1) == '-') {
i++;
}
}
count++;
}
즉, 만약 c= 와 c- 가 입력되어있다면 이를 하나의 문자로 보는 것이기 때문에
다음 반복문에서 = 이나 - 를 참조할 필요가 없다.
그렇기 때문에 해당 조건문을 만족하면 i 를 1 증가시켜 다음 문자를 건너 뛰게 해준다.
그리고 해당 반복문이 종료될 때마다 count 변수를 1 증가 시킨다.
3. 그럼 c 뿐만 아니라 다른 조건들도 작성해주자.
참고로 dz= 는 3 개의 문자를 하나의 문자로 보기 때문에 i 를 2 를 증가시켜주어야 한다.
String str = input();
int count = 0;
for (int i = 0; i < str.length; i++) {
char ch = str.charAt(i);
if(ch == 'c') { // 만약 ch 가 c 라면?
if(str.charAt(i + 1) == '=') { //만약 ch 다음 문자가 '=' 이라면?
// i+1 까지가 하나의 문자이므로 다음 문자를 건너 뛰기 위해 1 증가
i++;
}
else if(str.charAt(i + 1) == '-') {
i++;
}
}
else if(ch == 'd') {
if(str.charAt(i + 1) == 'z') {
if(str.charAt(i + 2) == '=') { // dz= 일 경우
i += 2;
}
}
else if(str.charAt(i + 1) == '-') { // d- 일 경우
i++;
}
}
else if(ch == 'l') {
if(str.charAt(i + 1) == 'j') { // lj 일 경우
i++;
}
}
else if(ch == 'n') {
if(str.charAt(i + 1) == 'j') { // nj 일 경우
i++;
}
}
else if(ch == 's') {
if(str.charAt(i + 1) == '=') { // s= 일 경우
i++;
}
}
else if(ch == 'z') {
if(str.charAt(i + 1) == '=') { // z= 일 경우
i++;
}
}
count++;
}
print(count);
4. 완성이 된 것 같다.
마지막에 count 를 출력해주면 될까..?
이 알고리즘대로 작성해서 만들면 자바의 경우 다음과 같은 에러를 발견할 수 있을 것이다.
java.lang.StringIndexOutOfBoundsException
즉 인덱스 참조 범위를 벗어났다는 것이다. ( = 참조할 수 없는 범위 )
왜일까?
예로들어 이러한 문자를 받았다고 생각해보자.
aedzdz=ls=c
그림으로 보자면 다음과 같을 것이다.
한번 위의 알고리즘대로 하나씩 해보면 알 수 있을 것이다.
i 가 10 일 때 반복문을 생각해보자.
그럼 str.charAt(10) 을 통해 ch 에 저장되고 ( ch = 'c' )
c 라는 문자를 받았기 때문에 첫 번째 조건문을 실행시키게 된다.
그리고 여기서 문제가 발생한다.
if( str.charAt(i + 1) == '=' )
aedzdz=ls=c 이 문자열에서 마지막 c 다음의 문자는 존재 하지 않는다.
그런데 charAt() 을 통해 참조하려고 하니까 StringIndexOutOfBoundsException 이라는 에러를 뱉는 것이다.
해결방법은 무엇일까?
간단하다. 현재 i 의 값이 문자열 길이(str.length) 에서 -1 값보다 작을 경우에만 다음 조건문을 실행시키면 된다.
( dz= 를 검사할 때는 i 가 str.length - 2 보다 작아야한다. )
그럼 이를 토대로 각 조건문에 새로운 조건문들을 작성해보자.
String str = input();
int count = 0;
for (int i = 0; i < str.length; i++) {
char ch = str.charAt(i);
if(ch == 'c') { // 만약 ch 가 c 라면?
if(i < str.length - 1) {
if(str.charAt(i + 1) == '=') { //만약 ch 다음 문자가 '=' 이라면?
// i+1 까지가 하나의 문자이므로 다음 문자를 건너 뛰기 위해 1 증가
i++;
}
else if(str.charAt(i + 1) == '-') {
i++;
}
}
}
else if(ch == 'd') {
if(i < str.length - 1) {
if(str.charAt(i + 1) == 'z') {
if(i < str.length - 2) {
if(str.charAt(i + 2) == '=') { // dz= 일 경우
i += 2;
}
}
}
else if(str.charAt(i + 1) == '-') { // d- 일 경우
i++;
}
}
}
else if(ch == 'l') {
if(i < str.length - 1) {
if(str.charAt(i + 1) == 'j') { // lj 일 경우
i++;
}
}
}
else if(ch == 'n') {
if(i < str.length - 1) {
if(str.charAt(i + 1) == 'j') { // nj 일 경우
i++;
}
}
}
else if(ch == 's') {
if(i < str.length - 1) {
if(str.charAt(i + 1) == '=') { // s= 일 경우
i++;
}
}
}
else if(ch == 'z') {
if(i < str.length - 1) {
if(str.charAt(i + 1) == '=') { // z= 일 경우
i++;
}
}
}
count++;
}
print(count);
이러면 참조가 벗어나는 일이 없게 된다.
이런식으로 구조를 짜면 되겠다.
- 풀이
- 방법 1
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str = in.nextLine();
int count = 0;
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if(ch == 'c') { // 만약 ch 가 c 라면?
if(i < str.length() - 1) {
if(str.charAt(i + 1) == '=') { //만약 ch 다음 문자가 '=' 이라면?
// i+1 까지가 하나의 문자이므로 다음 문자를 건너 뛰기 위해 1 증가
i++;
}
else if(str.charAt(i + 1) == '-') {
i++;
}
}
}
else if(ch == 'd') {
if(i < str.length() - 1) {
if(str.charAt(i + 1) == 'z') {
if(i < str.length() - 2) {
if(str.charAt(i + 2) == '=') { // dz= 일 경우
i += 2;
}
}
}
else if(str.charAt(i + 1) == '-') { // d- 일 경우
i++;
}
}
}
else if(ch == 'l') {
if(i < str.length() - 1) {
if(str.charAt(i + 1) == 'j') { // lj 일 경우
i++;
}
}
}
else if(ch == 'n') {
if(i < str.length() - 1) {
if(str.charAt(i + 1) == 'j') { // nj 일 경우
i++;
}
}
}
else if(ch == 's') {
if(i < str.length() - 1) {
if(str.charAt(i + 1) == '=') { // s= 일 경우
i++;
}
}
}
else if(ch == 'z') {
if(i < str.length() - 1) {
if(str.charAt(i + 1) == '=') { // z= 일 경우
i++;
}
}
}
count++;
}
System.out.println(count);
}
}
가장 기본적인 방법이다.
알고리즘은 위에서 설명했던 알고리즘과 같게 했다.
- 방법 2
BufferedReader 을 쓰는 방식이다.
그리고 위의 코드는 너무 복잡하니 조건문을 간략화하고 문자열의 길이를 변수로 바꿔서 좀 더 간략화 해보려 한다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine();
int len = str.length();
int count = 0;
for (int i = 0; i < len; i++) {
char ch = str.charAt(i);
if(ch == 'c' && i < len - 1) { // 만약 ch 가 c 라면?
//만약 ch 다음 문자가 '=' 또는 '-' 이라면?
if(str.charAt(i + 1) == '=' || str.charAt(i + 1) == '-') {
// i+1 까지가 하나의 문자이므로 다음 문자를 건너 뛰기 위해 1 증가
i++;
}
}
else if(ch == 'd' && i < len - 1) {
if(str.charAt(i + 1) == '-') { // d- 일 경우
i++;
}
else if(str.charAt(i + 1) == 'z' && i < len - 2) {
if(str.charAt(i + 2) == '=') { // dz= 일 경우
i += 2;
}
}
}
else if((ch == 'l' || ch == 'n') && i < len - 1) {
if(str.charAt(i + 1) == 'j') { // lj 또는 nj 일 경우
i++;
}
}
else if((ch == 's' || ch == 'z') && i < len - 1) {
if(str.charAt(i + 1) == '=') { // s= 또는z= 일 경우
i++;
}
}
count++;
}
System.out.println(count);
}
}
위 코드를 실행하면 Scanner 보다 BufferedReader 가 속도가 빠르니 시간이 단축된다.
그리고 if 문에 문자열 변환에 있어 공통적인 점은 조건식으로 묶어서 괄호로 우선순위를 정해주면 된다.
- 성능 차이
위에서 부터 순서대로
채점 번호 : 18673473 - BufferedReader
채점 번호 : 18673470 - Scanner
시간을 보면 BufferedReader 와 Scanner 의 성능차이 및 출력 방법에 따른 성능 차이 또한 볼 수 있다.
- 정리
항상 필자가 강조하는 부분이지만 만약 문제를 딱 보고 한번에 완벽하게 구상되는 것이 아니라면 하나씩 차근차근 짜보면서 완성해나가는 것이 좋다.
필자 보통 코드를 짤 때 한 번에 완성이 안되면 완성시키기 위해 3 가지 방법을 쓴다.
1. 큰 틀부터 하나씩 짠다.
2. 예외가 발생하는 경우를 쭉 나열한다.
3. 예외를 처리하고 및 극단적인 경우의 값을 넣어 디버깅 해본다.
대강 위 3 가지를 거치면 적어도 에러가 나는 일은 많이 줄 것이다.
'JAVA - 백준 [BAEK JOON] > 문자열' 카테고리의 다른 글
[백준] 1316번 : 그룹 단어 체커 - JAVA [자바] (45) | 2020.03.26 |
---|---|
[백준] 5622번 : 다이얼 - JAVA [자바] (28) | 2020.03.24 |
[백준] 2908번 : 상수 - JAVA [자바] (12) | 2020.03.20 |
[백준] 1152번 : 단어의 개수 - JAVA [자바] (33) | 2020.03.20 |
[백준] 1157번 : 단어 공부 - JAVA [자바] (42) | 2020.03.19 |