[BOJ] 2941_크로아티아 알파벳

gogori6565·2022년 7월 23일
0

문제
예를 들어, ljes=njak은 크로아티아 알파벳 6개(lj, e, š, nj, a, k)로 이루어져 있다. 단어가 주어졌을 때, 몇 개의 크로아티아 알파벳으로 이루어져 있는지 출력한다.

dž는 무조건 하나의 알파벳으로 쓰이고, d와 ž가 분리된 것으로 보지 않는다. lj와 nj도 마찬가지이다. 위 목록에 없는 알파벳은 한 글자씩 센다.


방법
두 가지 풀이 방법을 작성하겠다.
1) string 함수의 find(), replace() 함수 활용 코드
2) string 함수 사용하지 않고 푼 코드

  • if문 덕지덕지 사용하는 방법이 있는데 그러고 싶지 않아서 나름 체계적으로 설계한 switch문..
  • 사실 string 함수에 아직 익숙하지 않아서 공부하기 전까지는 2번이 최선인 줄 알았던 코딩 풋내기...


방법 1. string 함수의 find(), replace() 활용

풀이
입력받은 문자열에서 크로아티아 알파벳을 특정문자(한글자)로 바꾸고 전체 문자열 길이를 구하자.
ex) ljes=njak -> *e**ak (6글자)

  • find() 함수로 문자열에서 크로아티아 알파벳을 찾자.
  • replace() 함수로 find()로 찾은 크로아티아 알파벳의 첫 위치를 이용해 크로아티아 알파벳을 특정문자(*)로 교체한다.
  • 주의🛑 크로아티아 알파벳이 문자열에 여러 개 있을 수 있으므로 없을 때까지 찾아야한다. (find()의 반환값이 string::npos 인지 검사)

풀이 코드

#include<iostream>
#include<string>
#include<vector>
using namespace std;

int main(void)
{
    cin.tie(NULL); ios_base::sync_with_stdio(false);

    vector<string> croatia={"c=","c-","dz=","d-","lj","nj","s=","z="};
    int index;
    string str;
    cin>>str;

    for(int i=0;i<croatia.size();i++){
        while(1){ //크로아티아 알파벳이 더이상 문자열에 없을 때까지 반복
            index=str.find(croatia[i]);
            if(index==string::npos) //찾는 문자가 없는 경우
                break;
            str.replace(index,croatia[i].size(),"*");
        }
    }

    cout<<str.size();
}


방법 2. string 함수 사용하지 않고 푼 코드

풀이
문자열의 각 문자를 하나씩 검사해 if문으로 크로아티아 문자인지 아닌지 검사하는 방식
+) if-else 덕지덕지에서 좀 더 체계화되게 짜봤다.

크로아티아 문자에서
3글자는
dz=
2글자(뒷글자)는
(=) : c=, s=, z=
(-) : c-, d-
(j) : lj, nj

switch문으로 작성하여 매번 if-else가 아닌 적당한 시점에서 끊어주는 방식으로 작성할 생각.

switch 문을 작성하기에 앞서 우선순위를 고려하자. 우선순위의 기준은 가장 유일하게 나오는 순이다. 왜냐하면, switch문으로 작성하려는 이유 자체가 뒷글자가 일치할 경우 한 번에 검사(if)해서 알파벳 카운트를 할 생각이기 때문이다.

💡 알파벳 우선순위
1) d (유일한 세글자를 가지므로)
2) c
3) s, z, l, n
4) 나머지 알파벳

💡 뒷글자 우선순위
1) (d)z=
2) - (d- 때문)
3) = (2글자에 중복되는 c 때문)
4) j

우선순위 토대로 switch 문을 작성하면 대충 이러하다.

//cnt : 알파벳 count 변수, i : string 접근 index

case 'd':
	세글자인가? -> cnt++; i+=3; break;
    아니면 계속
case 'c':
	뒷글자가 '-'인가? -> cnt++; i+=2; break;
    아니면 계속
case 's':
case 'z':
	뒷글자가 '='인가? -> cnt++; i+=2; break;
	여기서 끊어야함! -> cnt++; i++; break; (ex) dj의 경우 하나로 이어지면 안되니까)
case 'l':
case 'n':
	뒷글자가 'j'인가? -> cnt++ i+=2; break;
default:
	cnt++; i++; break;

3글자, 2글자, 1글자 일 때, i+=3; i+=2; i++; 하는 이유는 간단하다.
string에 속한 다음 문자를 검사할 때, 3글자짜리 크로아티아 알파벳은 하나로 취급하니 3칸 뒤로 뛰어넘어야해서 i+=3; 인 것이다. 2글자, 1글자도 마찬가지 원리.


풀이 코드

#include<iostream>
#include<string>
using namespace std;

int main(void)
{
    cin.tie(NULL); ios_base::sync_with_stdio(false);

    int i=0,cnt=0;
    string str;
    cin>>str;

    //위 목록에 없는 알파벳은 한 글자씩 센다.
    //d= 같이 기호가 말도 안되게 나오는 경우 X
    while(i<str.size()){
        switch(str[i]){
            case 'd':
                //z만 검사하면 안돼. dza 일 경우 세 개 카운트임
                if(str[i+1]=='z'&&str[i+2]=='='){ //세글자
                    cnt++; i+=3; break;
                }
            case 'c':
                if(str[i+1]=='-'){ 
                    cnt++; i+=2; break;
                }
            case 's':
            case 'z':
                if(str[i+1]=='='){
                    cnt++; i+=2; break;
                }
                else{ //dj일 경우 두 개 카운트해야하니까 여기서 한 번 스탑
                    cnt++; i++; break;
                }
            case 'l':
            case 'n':
                if(str[i+1]=='j'){
                    cnt++; i+=2; break;
                }
            default:
                cnt++; i++; break;
        }
    }

    cout<<cnt;
    return 0;
}


string 함수를 아직 잘 모르기도 하고 익숙하지 않아서 어렵게 느껴졌던 문제같다. find() 함수와 replace() 함수를 사용하면 간단했던 문제! 그치만 우선순위 생각하면서 짜본 switch 코드도 if-else 덕지덕지 안하고 체계적으로 생각하며 짜는 과정 자체가 좋은 시도였다고 생각한다! 그래도 역시 string 함수 쓰는 게 더 깔끔ㅎ
문자열 다루는 문제가 나오면 문자열 함수를 적극 활용하라는 팁을 얻고 이번 문제는 여기서 마치겠다. 안녕!

문제 출처 : https://www.acmicpc.net/problem/2941

profile
p(´∇`)q

0개의 댓글