https://www.acmicpc.net/problem/1157

문제를 처음 보자마자 나만의 로직을 생각해보았다.
- 소문자 알파벳 갯수를 세어 '소문자 갯수 배열'에 저장한다.
- 대문자 알파벳 갯수를 세어 '대문자 갯수 배열'에 저장한다.
- 두 배열을 더해 새로운 '알파벳 갯수 배열'을 만든다.(기존 배열에 재활용 해도 된다.)
- '알파벳 갯수 배열'을 돌며 Max 값을 찾는다.
- cnt 변수를 선언하여, '알파벳 갯수 배열'을 돌며 Max와 같은 값이 있다면 cnt++를 해준다.
- Max == 1이면, '1' 출력, Max > 1 이면, "?" 출력.
위 로직대로 구현 후, 제출하였는데 당연히..
틀렸다.. 왜 틀렸을까 한번 생각해보니 나의 로직에는 몇 가지의 문제점이 있었다..
- 배열/for문을 너무 많이 사용한다.
- Max가 1이던 1보다 크던, 해당 Max값이 아닌, Max에 해당하는 알파벳을 출력해야한다.
- Max에 해당하는 알파벳을 출력하기 위한 사전작업(배열이나 for문 등)이 전혀 이루어지지 않았다.
#include <iostream>
#include <string>
using namespace std;
int main() {
int arr[26] = {0, };
string s;
getline(cin, s);
// 해당 단어 '대, 소문자' -> '대문자' 로 변환
for(int i = 0; i < s.length(); i++){
if(s[i] >= 'a' && s[i] <= 'z'){
s[i] = s[i] - 'a' + 'A';
// 등장한 알파벳 갯수 세기, 65를 빼는 건 대문자로 변환 후 체크하기 때문.
}
arr[s[i] - 65]++;
}
// for(int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){
// cout << arr[i] << endl;
// }
// max인 알파벳 출력을 위한 변수 alpha
int alpha = 0;
int max = 0;
// max값 찾기 위한 변수 max
for(int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){
if(arr[i]> max){
max = arr[i];
alpha = i;
}
}
// max값이 중복되는지 체크하기 위한 변수 cnt
int cnt = 0;
for(int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){
if(arr[i] == max){
cnt++;
}
}
if(cnt == 1){
cout << (char)(alpha + 65);
}
else{
cout << '?';
}
return 0;
}
솔루션을 완성하는 시간이 생각보다 되게 오래 걸렸다..(1시간 정도?) 이유는,
아스키 코드에 대해 부족한 지식
그냥 잘 몰랐다.. 앞으로 문자열을 다루거나 코딩테스트를 위해 대문자는 65 ~ , 소문자는 97~ 라는 것을 꼭 기억해야겠다.(26개니까, +26-1을 해주면 각 끝 값이겠지?)
코딩실수(1)
s[i] = s[i] - 'a' + 'A';이 코드는 문자열 s의 i번째 요소를 대문자로 변환하는 코드이다. 먼저 s[i]에서 'a'를 빼고, 그 결과에 'A'를 더함으로써 해당 문자가 소문자였던 경우 대문자로 변환된다.
s[i] -= 'a' + 'A';하지만, 이 코드는 문자열 s의 i번째 요소에서 'a'와 'A'를 더한 값을 빼는 것이다. 이로써 해당 문자열 요소의 값이 변경되는데, 이 코드는 문자열 s의 i번째 요소에 대문자와 소문자를 혼합한 결과를 반영하여 원래 문자열 s가 어떤 문자로 시작하든 결과가 예측하기 어려운 값으로 출력된다.
실제로 소문자 -> 대문자 변환이 제대로 되지 않았으며, 출력값도 이상하였다..
코딩실수(2)
for(int i = 0; i < sizeof(arr); i++){ if(arr[i]> max){ max = arr[i]; alpha = i; } }해당 코드를 보면, 배열의 실제 길이(length)를 구하는 코드이지만, 이는 정확한 길이를 구하느 것이 아니다. sizeof(arr)는 배열 arr의 크기를 바이트 단위로 반환하는데, 여기서 arr은 int 형 배열이며, 각 int는 4바이트(32 비트)를 차지한다.
따라서, 총 26개의 int가 들어있으므로, sizeof(arr)는 26 * 4 = 104가 되었던 것이다.
for(int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){ if(arr[i]> max){ max = arr[i]; alpha = i; } }결론적으로, 배열 arr의 크기를 출력할 때는 sizeof(arr) / sizeof(arr[0])와 같이 배열 내 요소의 크기로 나누어한다.
코딩실수(3)
for(int i = 0; i < s.length(); i++){ if(s[i] >= 'a' && s[i] <= 'z'){ s[i] = s[i] - 'a' + 'A'; // 등장한 알파벳 갯수 세기, 65를 빼는 건 대문자로 변환 후 체크하기 때문. arr[s[i] - 65]++; } }해당 코드는 s[i]가 소문자일때, 대문자로 변환해주는 부분이다. 그러나 알파벳 등장 횟수를 count 하는 arr[s[i] - 65]++ 이 부분이 if문 안에 있어, 대문자 그대로 들어올 경우 횟수를 count하지 않았다.
for(int i = 0; i < s.length(); i++){ if(s[i] >= 'a' && s[i] <= 'z'){ s[i] = s[i] - 'a' + 'A'; // 등장한 알파벳 갯수 세기, 65를 빼는 건 대문자로 변환 후 체크하기 때문. } arr[s[i] - 65]++; }이렇게 if문 밖에 빼서 모든 대/소문자에 대해 count할 수 있게 해주었다.
구글에만 검색해도 수많은 솔루션들이 있다.. 나보다 훨씬 효율적으로 짠 사람들도 많은데, 그들의 솔루션을 참고하지 않았다. (중간 과정을 cout으로 확인하며, stackoverflow에서 질문을 뒤지긴 했다.)
아직은.. 그래도 아직은 투박할진 몰라도 이대로 코딩하면서 배우는 게 좋다..