문제
예를 들어, ljes=njak은 크로아티아 알파벳 6개(lj, e, š, nj, a, k)로 이루어져 있다. 단어가 주어졌을 때, 몇 개의 크로아티아 알파벳으로 이루어져 있는지 출력한다.
dž는 무조건 하나의 알파벳으로 쓰이고, d와 ž가 분리된 것으로 보지 않는다. lj와 nj도 마찬가지이다. 위 목록에 없는 알파벳은 한 글자씩 센다.
방법
두 가지 풀이 방법을 작성하겠다.
1) string 함수의 find(), replace() 함수 활용 코드
2) string 함수 사용하지 않고 푼 코드
풀이
입력받은 문자열에서 크로아티아 알파벳을 특정문자(한글자)로 바꾸고 전체 문자열 길이를 구하자.
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();
}
풀이
문자열의 각 문자를 하나씩 검사해 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 함수 쓰는 게 더 깔끔ㅎ
문자열 다루는 문제가 나오면 문자열 함수를 적극 활용하라는 팁을 얻고 이번 문제는 여기서 마치겠다. 안녕!