[백준/C++] 1157번 단어공부

Dandyoung·2023년 10월 25일
0

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

문제를 처음 보자마자 나만의 로직을 생각해보았다.

  1. 소문자 알파벳 갯수를 세어 '소문자 갯수 배열'에 저장한다.
  2. 대문자 알파벳 갯수를 세어 '대문자 갯수 배열'에 저장한다.
  3. 두 배열을 더해 새로운 '알파벳 갯수 배열'을 만든다.(기존 배열에 재활용 해도 된다.)
  4. '알파벳 갯수 배열'을 돌며 Max 값을 찾는다.
  5. cnt 변수를 선언하여, '알파벳 갯수 배열'을 돌며 Max와 같은 값이 있다면 cnt++를 해준다.
  6. Max == 1이면, '1' 출력, Max > 1 이면, "?" 출력.

위 로직대로 구현 후, 제출하였는데 당연히..

틀렸다.. 왜 틀렸을까 한번 생각해보니 나의 로직에는 몇 가지의 문제점이 있었다..

  1. 배열/for문을 너무 많이 사용한다.
  2. Max가 1이던 1보다 크던, 해당 Max값이 아닌, Max에 해당하는 알파벳을 출력해야한다.
  3. 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에서 질문을 뒤지긴 했다.)

아직은.. 그래도 아직은 투박할진 몰라도 이대로 코딩하면서 배우는 게 좋다..

profile
코딩이어려운당신에게,,

0개의 댓글