All is well

[백준/C++] 10808(알파벳 개수) (아스키 코드 / 범위 기반 for문 / constexpr / 삼항연산자) 본문

C++/BAEKJOON

[백준/C++] 10808(알파벳 개수) (아스키 코드 / 범위 기반 for문 / constexpr / 삼항연산자)

D0YUN 2025. 3. 8. 15:55

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

 

 

내가 해냄

#include <bits/stdc++.h>
using namespace std;

string s;   // 입력받을 문자열 선언

// 알파벳 개수가 기억이 안나서 아스키코드 값을 이용하여 배열 크기 지정
// 소문자 개수만큼 배열을 선언하여 각 알파벳의 개수를 저장
int cnt['z' - 'a' + 1] = {0};   

int main()
{
    /*
      배열의 크기 확인 방법:sizeof(배열) / sizeof(배열의 첫 번째 요소)
      - 배열 전체 크기를 각 요소의 크기로 나누어 개수를 구할 수 있음.
    */
    // cout << sizeof(cnt) / sizeof(cnt[0]) << "\n";  // 배열 크기 확인용 코드 (디버깅용)
    
    // 문자열 s를 입력받는다
    cin >> s;

    /*
      입력받은 문자열을 순회하면서 각 알파벳의 등장 횟수를 카운트
      - s[i]는 문자(char)이며, 이를 'a'와의 차이를 이용해 배열의 인덱스로 변환
      - 'a'는 ASCII 값 97이므로, 'a'~'z'를 0~25 범위의 인덱스로 변환 가능
      - 예: 'b' - 'a' → 1, 'z' - 'a' → 25
   */
    for (int i = 0; i < s.size(); i++)
        cnt[s[i] - 'a']++;      

    /*
      배열에 저장된 각 알파벳의 등장 횟수를 출력
      - 범위 기반 for문을 사용하여 cnt 배열의 모든 값을 출력
      - 값들 사이에는 공백을 출력하여 가독성을 높임
    */
    for(int i : cnt) cout << i << " ";

    return 0;
}

 

사용한 핵심 개념

// 배열을 이용한 카운팅 (Counting Array)

  • `cnt` 배열을 활용하여 각 알파벳의 등장 횟수를 저장하는 방식이다.
  • 공간 복잡도가 O(1)로 빠른 조회 및 업데이트가 가능하다.
  • 해시 테이블(`unordered_map`) 대신 고정된 크기의 배열을 활용하여 더 빠르게 동작한다.
  • 코드에서 활용
int cnt[26] = {0};   // 26개의 공간을 갖는 배열 선언
for (int i = 0; i < s.size(); i++)
    cnt[s[i] - 'a']++;  // 해당 알파벳 인덱스 증가

 

// ASCII 코드 값 이용 (s[i] - 'a')

  • 문자 'a'부터 'z'까지의 ASCII 코드 값을 활용하여 배열의 인덱스로 변환하는 기법이다.
  • 'a'의 ASCII 값은 97, 'b'는 98, ..., 'z'는 122이므로 'a'를 기준으로 빼면 0~25 범위의 정수 인덱스를 얻을 수 있다.
char c = 'c';
cout << c - 'a';  // 결과: 2 ('c' - 'a' = 99 - 97)

 

  • 코드에서 활용
cnt[s[i] - 'a']++;  // 알파벳을 0~25 범위의 인덱스로 변환하여 개수 증가

 

// 범위 기반 for문 이용 (for(int i : cnt))

  • C++11 이상에서 추가된 문법으로, `std::vector` 또는 배열을 순회할 때 유용하다.
  • 기존의 for 루프에서 인덱스를 활용하는 방식보다 가독성이 좋고 코드가 간결하다.
// 기존의 인덱스 기반 for문
for (int i = 0; i < 26; i++) {
    cout << cnt[i] << " ";
}

// 범위 기반 for문 (더 간결하다)
for (int i : cnt) {
    cout << i << " ";
}
  • 코드에서 활용
for (int i : cnt) cout << i << " ";

AI 피드백

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

constexpr int ALPHA = 26;   // 알파벳 갯수
/*
constexpr : 컴파일 타임 상수를 정의하는 키워드
- constexpr을 사용하면 반드시 컴파일 시점에 값이 결정됨
- const는 런타임에도 값이 결정될 수 있지만, constexpr은 무조건 컴파일 타임에 결정되어야 한다
*/

int cnt[ALPHA] = {0};   // 각 알파벳 개수를 저장할 배열 (초기값 0)

int main()
{
    string s;

    // 문자열을 입력한다
    cin >> s;
    
    // 문자열의 각 문자를 순회하며 알파벳 개수를 센다
    // - string은 char로 구성되어 있다.
    // - 이를 범위 기반 for문에 활용하여 각 알파벳 소문자의 갯수를 센다
    // - 아스키코드 값을 이용해 cnt 배열의 해당 알파벳 위치에 접근하여 개수를 증가시킨다
    for(char c : s) cnt[c - 'a']++;     

    // 각 알파벳의 개수를 출력한다
    // - 삼항 연산자를 활용하여 마지막 값 뒤에는 개행, 나머지는 공백을 출력한다
    for(int i = 0; i < ALPHA; i++)
        cout << cnt[i] << (i == ALPHA - 1 ? "\n" : " ");

    return 0;
}

 

 

사용한 핵심 개념

// `constexpr`을 활용한 컴파일 타임 상수 정의

  • `constexpr` 키워드를 사용하여 컴파일 타임에 결정되는 상수를 정의하는 방식이다.
  • `const`는 런타임에도 값이 결정될 수 있지만, `constexpr`은 반드시 컴파일 시점에 값이 확정되어야 한다.
  • 매직 넘버(하드코딩된 숫자)를 없애고 의미를 명확하게 표현할 수 있다.
  • 코드에서 활용
constexpr int ALPHA = 26;   // 알파벳 개수 (a~z는 총 26개다)
  • 기존 코드와의 차이점
// 기존 코드에서는 'z' - 'a' + 1로 배열 크기를 정의했지만, 
// ALPHA라는 상수를 사용하면 더 직관적이고 유지보수가 편리하다.
int cnt[ALPHA] = {0};

 

// range-based for loop (범위 기반 for문) 활용

  • `for (char c : s)`와 같은 구문을 사용하여, 문자열의 각 문자를 직접 순회하는 방식이다.
  • 기존의 인덱스 기반 for 루프보다 더 직관적이고 간결하며, 가독성이 뛰어나다.
  • 코드에서 활용
for (char c : s) cnt[c - 'a']++;  // 문자를 직접 순회하며 카운트한다.

 

  • 기존 코드와의 차이점
// 기존 코드
for (int i = 0; i < s.size(); i++)
    cnt[s[i] - 'a']++;  

// 개선된 코드 (더 간결해졌다.)
for (char c : s) cnt[c - 'a']++;

 

// 삼항 연산자 활용하여 출력 최적화

  • 마지막 원소 출력 시 불필요한 공백을 방지하기 위해 삼항 연산자(?:)를 사용한 방식이다.
  • if-else 문을 사용하는 것보다 더 간결하고 깔끔한 코드가 된다.
  • 코드에서 활용
for (int i = 0; i < ALPHA; i++)
    cout << cnt[i] << (i == ALPHA - 1 ? "\n" : " ");
  • 기존 코드와의 차이점
// 기존 코드에서는 모든 출력값 뒤에 공백이 들어갔다.
for (int i : cnt) cout << i << " ";

// 개선된 코드에서는 마지막 값 뒤에 개행(\n)을 넣고, 나머지는 공백을 출력한다.
for (int i = 0; i < ALPHA; i++)
    cout << cnt[i] << (i == ALPHA - 1 ? "\n" : " ");