[백준] 2941번 : 크로아티아 알파벳- Java(자바)

이정우·2021년 9월 7일
0

백준

목록 보기
13/32

이번 문제는 쉽게 말해서 영어 소문자와 -,=으로만 이루어진 문자열을 입력 받았을 때 크로아티아 알파벳 목록에 존재하면 1개로 카운팅을 올려주고 존재하지 않는 기타 알파벳들은 하나씩 카운팅 하는 문제였습니다.
어제부터 시작해서 10시간 가까이 몇 번을 갈아엎다가 겨우 풀었네요.

접근 방법

1. 초기 접근 방법 : for문을 사용하여 문자열을 하나하나 찾아서 크로아티아 문자가 보이면 0으로 치환해준 후 계산하기. (0으로 치환하는 이유는 만약에 ddz=z=을 보면 ddz=z= 의 굵은 글씨 부분이 사라지고 나면 dz=의 모양이 또 만들어지므로 이런 경우를 방지하기 위해)

->> 하지만 for문이 겹치다가 도저히 되돌릴 수 없는 지경에 이르러 코드를 갈아 엎었습니다..(약 7시간)

  1. 후반 접근 방법 : 굳이 데이터를 새로운 데이터로 치환하거나 삭제하면 for문 돌릴 때 더욱 힘들어지므로 아예 처음부터 문자를 삭제하는 대신 크로아티아 알파벳이 존재한다면 count의 숫자만 올려주어 불필요한 오류를 없애는 식으로 코드 다이어트를 시도.

->> 어느정도 잘 돌아갔으나 중간에 문자가 겹쳐지는 오류 발생. 예를 들어서 코드에서는 문자열의 0번째 문자 + 1번째 문자을 한 문자와 크로아티아 문자 목록을 비교하는 식으로 하였는데 dz=ak 을 보면 dz=이 크로아티아 문자인데 이후 z=도 크로아티아 문자로 인식해버림. 이를 해결하길 위해 for문에서 변수로 선언한 i의 값을 강제적으로 올려줌.

import java.util.Scanner;

public class Croatia_Alphabet {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		String croa = sc.next();
		int cha = 0; //크로아티아 문자 목록에 존재하면 그 만큼 차감
		int count = 0; // 크로아티아 문자 사용 횟수
		String[] croa_al = { "z=", "c-", "c=", "d-", "lj", "nj", "s=", "dz=" }; //크로아티아 문자 목록
		for (int i = 0; i < croa.length() - 1; i++) { //문자열 길이의 -1 만큼 반복. 예를 들어 5글자라 치면 0~4번까지 인덱스 번호라고 생각하고 01 12 23 34 .. 총 4번이 돌아가게 됨. 45는 범위 초과. 그러므로 -1을 해줌
			for (int k = 0; k < croa_al.length; k++) { //각 인덱스 번호별로 크로아티아 문자 목록을 전부 집어넣어 주기 위해 for문 사용
				if (i + 1 <= croa.length() - 1) { //코드 중간 중간 i++로 인해 i값이 초과되어서 범위 초과 오류가 뜨는 것을 방지하기 위해 범위 확인.
					if ((Character.toString(croa.charAt(i)) + Character.toString(croa.charAt(i + 1))) // 2글자씩 합쳐서 크로아티아 목록과 맞는지 확인 후 맞으면 count 더해줌
							.equals(croa_al[k])) {
						count++;
						cha = cha + 2; //크로아티아 문자를 따로 삭제하지 않았기 때문에 나중에 문자열의 길이를 구할 때 크로아티아 문자를 빼주기 위해 -2
						i++; //만약 5글자 입력되어서 0~4인덱스가 만들어졌는데 0 , 1 인덱스에서 크로아티아 문자가 존재했다면 그 다음에 1 , 0으로 가서 중첩 확인을 하면 안되기 때문에 +1로 바로 넘어가줌.
					}
				} //반복
				if (i + 2 <= croa.length() - 1) {
					if ((Character.toString(croa.charAt(i)) + Character.toString(croa.charAt(i + 1))
							+ Character.toString(croa.charAt(i + 2))).equals(croa_al[k])) {
						count++;
						i++;
						cha = cha + 3;
					}
				}

			}
		}

		int le = croa.length() - cha; //총 길이에서 크로아티아 문자 존재 수 만큼 빼줌
		System.out.println(count + le); //총 크로아티아 문자가 사용된 횟수 + le
	}
}

셋팅 작업

우선은 croa에 문자열을 입력받았습니다. cha 변수는 크로아티아 문자를 삭제하지 않고 그대로 둘 것이기 때문에 나중에 길이를 구할 때는 크로아티아 문자의 존재 수 만큼 줄여주기 위해 만들었습니다.
(dz=ak을 예로 보자면 dz=은 크로아티아 문자이지만 삭제하지 않고 그대로 문자열에 놔둠. 하지만 나중에 카운팅 총 합을 구할 때 길이가 필요하므로 실질적인 길이, 즉 크로아티 문자가 사라졌을 때의 길이를 구할 때 -를 해주기 위해서 변수 사용)
count 변수는 크로아티아 문자가 쓰인 횟수를 카운팅 해주기 위해 만들었습니다. 그리고 croa_al 배열에 크로아티아 문자 목록을 넣어 사용했습니다.

실질적인 구현

  • 첫 for문에서는 문장 길이 -1을 해주었는데 그 이유는 예를 들어 길이 5인 문자를(abcde) 두 글자씩 묶어서 확인한다 생각하고 길이 인덱스를 0~4라고 생각한다면 0+1(ab), 1+2(bc), 2+3(cd), 3+4(de) 총 4번 확인이 가능하므로 -1을 해주었습니다.
for (int i = 0; i < croa.length() - 1; i++)
  • 그 다음 for문을 사용하여 크로아티아 목록에 존재하는 단어들을 하나씩 비교하였습니다.
for (int k = 0; k < croa_al.length; k++)
  • 이때, 코드 중간에 i++을 선언하여서 범위 초과 오류가 발생할 수 있으므로 if문으로 범위 확인을 하였습니다.
if (i + 1 <= croa.length() - 1)
  • 입력받은 문자열을 앞에서 부터 0,1 -> 1,2 -> 2,3 ...이런 식으로 순차적으로 찾아갈 것이기 때문에 변수 i를 이용해서 문자열.charAt()메서드로 문자 1개씩 총 2개를 뽑아냈습니다. 그 후에는 두 문자를 더해줘서 equlas 메서드를 사용하기 위해 Character.toString메서드를 사용해 문자열로 다시 만들어 주었습니다. 그 후 크로아티아 문자 목록과 비교를 진행하였습니다.

    if ((Character.toString(croa.charAt(i)) + Character.toString(croa.charAt(i + 1))) .equals(croa_al[k]))
  • 크로아티아 문자 2개씩 검사할 때는 cha에 +2를 3개씩 검사할 때는 +3을 해주었습니다. 그리고 문자 중복을 막기 위해서 i++을 해줬습니다. 문자 중복이란 예를 들어 ddz=z=가 입력 되었을 때 ddz=z= dz=부분이 크로아티아 문자인데 다음 검사 때 ddz=z= 이미 방금 사용된 z를 포함해서 z=을 크로아티아 문자로 인식할 수 있는 문제입니다. 이를 막기 위해 만약 크로아티아 문자를 만났다면 i++을 해줘서 0,1 1,2 2,3 ..순서로 돌아가는 프로그램을 0,1에서 크로아티아 만나면 바로 i++해줘서 2,3으로 넘어가게 해주었습니다.

    i++;
    cha = cha + 2;
  • 위 방식으로 문자열 2개씩 확인을 진행하면서 크로아티아 3개짜리 문자열도 진행하였습니다. 그 이유는 크로아티아 문자 목록에 3글자 짜리가 하나 존재하기 때문입니다.

if (i + 2 <= croa.length() - 1) {
	if ((Character.toString(croa.charAt(i)) + Character.toString(croa.charAt(i + 1))
	+ Character.toString(croa.charAt(i + 2))).equals(croa_al[k])) {
	count++;
	i++;
	cha = cha + 3;
	}
}
  • 마지막으로 크로아티아 문자 사용 횟수 + 크로아티아 문자가 빠진 문자열의 길이를 통해 결과값을 얻기 위해 다음과 같이 변수들을 사용해주었습니다.
int le = croa.length() - cha; //총 길이에서 크로아티아 문자 존재 수 만큼 빼줌
System.out.println(count + le); //총 크로아티아 문자가 사용된 횟수 + le

끝으로..

처음 생각할 때는 적당한 문제 같았는데 풀수록 여러 문제가 튀어나왔습니다. 중간에 comparator등 여러 가지 처음 보는 기능등을 사용해 볼 수 있는 시간이었던거 같습니다. 참 재밌고 중간 중간 힘들었는데 실력 향상도 많이 되었고 마지막 백준에서 정답 처리 될 때 참 즐거웠던거 같습니다. 시간이 오래 걸려서 어제 못 올리고 오늘 올린게 아쉬움으로 남았습니다. 다른 분들 코드를 보니 저는 아예 두 글자를 입력 받아 한번에 처리 했는데 1글자씩 받아서 처리를 하시는걸 볼 수 있었습니다. 예를 들어서 c를 찾았다면 다음 글자가 =가 올 경우에만 카운팅 해주는 식 이었습니다. 이 방법도 i++을 사용해 중복을 피하는 걸 보고 많이 반가웠습니다.

++ 추가
다른 분들 코드 보다가 신박한걸 봤습니다. 문자열을 입력 받고 해당 문자열에 replace를 사용하는데 크로아티아 문자 표에 있는 문자들이 입력되면 숫자로 바꿔주게 하는거 였습니다. dz=을 z=보다 앞에 replace해서 중복을 피한거 같습니다. 예를 들어 dz=fge가 입력되면 0fge로, dz=z=fge면 01fge같은 형식으로 바꿔 버렸습니다. 그 후 해당 문자열의 길이를 반환해줬습니다. 아무튼 코드도 간결하고 보기 좋은거 같고 역시 머리를 잘 써야 하는거 같습니다.

출처 : 백준 2941번 https://www.acmicpc.net/problem/2941

profile
프로그래밍 공부 중!

0개의 댓글