이번 문제는 쉽게 말해서 영어 소문자와 -,=으로만 이루어진 문자열을 입력 받았을 때 크로아티아 알파벳 목록에 존재하면 1개로 카운팅을 올려주고 존재하지 않는 기타 알파벳들은 하나씩 카운팅 하는 문제였습니다.
어제부터 시작해서 10시간 가까이 몇 번을 갈아엎다가 겨우 풀었네요.
접근 방법
1. 초기 접근 방법 : for문을 사용하여 문자열을 하나하나 찾아서 크로아티아 문자가 보이면 0으로 치환해준 후 계산하기. (0으로 치환하는 이유는 만약에 ddz=z=을 보면 ddz=z= 의 굵은 글씨 부분이 사라지고 나면 dz=의 모양이 또 만들어지므로 이런 경우를 방지하기 위해)
->> 하지만 for문이 겹치다가 도저히 되돌릴 수 없는 지경에 이르러 코드를 갈아 엎었습니다..(약 7시간)
->> 어느정도 잘 돌아갔으나 중간에 문자가 겹쳐지는 오류 발생. 예를 들어서 코드에서는 문자열의 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 (int i = 0; i < croa.length() - 1; i++)
for (int k = 0; k < croa_al.length; k++)
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