본문 바로가기
C#/C# : Baekjoon

[백준] C# : 단어 공부(1157번)

by TODAYCODE 2021. 11. 7.
반응형

백준 단계별로 풀어보기 7단계 5번문제

 

1157번: 단어 공부

알파벳 대소문자로 된 단어가 주어지면, 이 단어에서 가장 많이 사용된 알파벳이 무엇인지 알아내는 프로그램을 작성하시오. 단, 대문자와 소문자를 구분하지 않는다.

www.acmicpc.net

문제의 핵심

문제 풀이를 위해 적절한 배열이나 변수를 만들어서 사용할 수 있는가?

 


내 생각엔 브론즈 문제 중에서 가장 어렵지 않나 생각이 든다.

사실 브론즈 단계를 넘어서기만해도,

BFS나 길찾기 알고리즘 같은 문제를 다루면 이렇게 디자인하는게 일반적이게 되지만,

아직 알고리즘을 제대로 다뤄보지 않은 채로 이걸 해결 하기에는 어려울 수 있다.

 

만약 이 문제에서 막혔다면

내가 할 수 있는 것들을 하나씩 처리한 뒤에

막히는 부분에서는 내가 뭘해야할지, 어떻게 해야할지를 하나씩 처리하는게 좋다.

 

문제풀이

일단 할 수 있는 것 부터 처리하자.

1. 대소문자를 구분하지 않는다고 했으니 입력받은 문자를 대문자나 소문자로 전부 변경하자.

ToUpper() 혹은 ToLower()를 사용해서 변경할 수도 있다.

(사실 대문자or소문자로 통일하지 않아도 풀 수는 있다... 내가 그랬다...

근데 통일시키는게 훨씬훨씬 편하니까 통일시키자.)

 

자 그럼 이제 무엇을 해야할까?

입력된 문자를 하나씩 체크하면서 그 문자가 어떤 알파벳인지를 파악해야한다.

2. 이걸 위해서는 알파벳을 담고있는 배열이 하나 필요할 것이다.

for문을 돌면서 입력된 문자를 하나씩 가져오고,

그 안에서 다시 for문을 돌면서 알파벳배열을 하나씩 가져와서 비교하면,

입력된 문자가 어떤 알파벳인지를 구분할 수 있을테니까.

따라서 알파벳을 전부 가지고있는 배열을 만든다.

배열에 알파벳을 담는 방법은 굉장히 많다.

하드코딩으로 직접 하나씩 알파벳을 타이핑해서 생성해도 되지만,

나는 그냥 26칸을 가진 Char형태의 빈 배열을 만들고 for문을 돌려서 A부터 Z까지 들어가도록 만들었다.

for (int i = 0; i < 26; i++)  {
            Max[i] = (char)((int)'A' + i);  }  

배열을 만드는 것은 본인 마음이니 알아서 만들자.

아마 속도는 직접 알파벳을 타이핑하는게 가장 빠를 듯.

 

그런데 여기서 문제가 있다.

위의 두가지를 사용해서 현재 문자의 알파벳을 파악했다고 치더라도

그 정보를 어떻게 저장해둘까?

3. 이 정보를 저장하기 위해서 또다른 배열이 하나 더 필요하다.

이게 굉장히 중요한 포인트이다.

똑같이 알파벳 숫자와 동일한 26개짜리의 int형 배열을 만든다.

이제 이 배열은 1번은 A와 대응하고 마지막 26은 Z와 대응하는 배열이라고 생각하면 된다.

만약 위에서 첫번째 문자가 C였다는게 밝혀졌다면,

C에 해당하는 인덱스는 3번이므로 지금 만든 배열의 3번 인덱스의 숫자에 1을 더해준다.

 

즉, 이중 for문을 돌면서 i번째의 문자가 어떤 알파벳인지 파악했다면,

해당 알파벳배열의 인덱스값에 해당하는 int형의 배열의 값을 1씩 증가시키고

이 모든 과정을 끝내면 모든 문자를 검사하면서 등장한 횟수만큼 int형 배열의 값이 바뀌어있을 것이다.

// 입력된 문자열을 하나씩 반복
for (int i = 0; i < t.Length; i++) {
// 대문자를 A부터 Z까지 반복
for (int j = 0; j < Max.Length; j++) {
// 검사중인 문자가 A ~ Z중 무엇인지 찾기
if (t[i] == Max[j]) {
                // 일치하는 경우 해당 알파벳번호 index의 숫자를 증가시킴.
answer[j]++;

자 그러면 이제 입력받은 문자를 알파벳별로 몇번 등장했는지를 배열에 정보로 담아냈다.

이제 남은 것은 정보가 담긴 배열을 읽어서 문제에서 요구하는 방식의 출력을 해주면 된다.

간단하게 생각하면 answer배열을 하나씩 확인하면서 가장 높은 숫자가 존재하는 인덱스를 찾고

해당 인덱스에 해당되는 알파벳을 출력하면 된다.

다만, 최대값이 중복으로 등장하면 ?를 출력해야하므로 검사하는 동안에 최대값을 비교할 변수를 또다시 만들어야하고,

중복이 발생했다는 정보를 저장할 bool same = false 도 하나 선언해준다.

int high = 0; 이라는 변수를 만들어놓고 최대값이 발생할 때마다 high에 입력해준다.

그럼 for문을 돌면서 현재i번째의 값이 high보다 작으면 무시하고 넘어가면 되고,

만약 값이 동일하다면 same 변수를 true로 바꿔준다.

만약 더 크다면 high를 현재값으로 변경하고 same변수를 false로 바꿔준다.

여기서 값이 0인 경우는 무시하도록 해줘야 더 효과적이다.

그리고 위에 적은 것처럼 코드를 디자인할 때 주의할 점이 또 존재한다.

단순하게 값이 같을 때 same을 true만 바꿔주기만 한다면 문제가 생긴다.

aabbccc 같은 경우에 오류가 발생한다.

aabb까지 처리했을 때 same이 true가 되는데 이후에 ccc가 등장하면서 더 큰 high값이 나중에 등장할 수도 있다.

따라서 최초의 high값이 등장할땐 same을 false로 바꿔줘야 문제가 없다.

 

끝으로 이 과정을 종료한 뒤에,

조건문을 사용해서 same의 true/false에 따라서 적절한 답을 출력하도록 만들면 된다.

 

정답 코드

using System;

class 단어공부
{
// 각각의 배열 생성
    static int[] answer = new int[26];
    static char[] Max = new char[26];

    static void Main()
    {
    // 각 배열에 문자열을 채워준다.
        for (int i = 0; i < 26; i++)
        {
            Max[i] = (char)((int)'A' + i);
        }  
        // 문자를 입력받아서 전부 대문자로 변경.
        string text = Console.ReadLine().ToUpper();
        // 중복이 몇번인지 세어주는 함수
        Count(text);
        // 결과값 출력 함수
        Console.WriteLine(Result());
    }

	// 입력된 문자열에 중복값이 최대가 몇인지 파악하는 함수
    static void Count(string t)
    {
    // 입력된 문자열을 하나씩 반복
        for (int i = 0; i < t.Length; i++)
        {
        // 대문자를 A부터 Z까지 반복
            for (int j = 0; j < Max.Length; j++)
            {
            // 검사중인 문자가 A ~ Z중 무엇인지 찾기
                if (t[i] == Max[j])
                {
                // 일치하는 경우 해당 알파벳번호 index의 숫자를 증가시킴.
                    answer[j]++;
                    // 이를 통해서 각각 알파벳이 몇번 나왔는지 알 수 있다.
                }
            }
        }
    }

    static char Result()
    {
    // 최대값이 존재하는 인덱스를 저장할 변수
        int num = 0;
        // 현재 최대값을 저장할 변수
        int high = 0;
        // 최대값이 여러개인 경우 true가 됨
        bool same = false;

        for (int i = 0; i < answer.Length; i++)
        {
            // 중복이 발생한 다음에 뒤에서 더 큰수가 발생할 수 있다.
            
            // answer의 값이 현재 최대치와 동일할 경우
            if (answer[i] == high && answer[i] != 0)
            {
            // 동일값이 존재.
                same = true;
            }
            // 만약 현재 값이, 지금까지의 최대값보다 크다면
            if (answer[i] > high)
            {
            // 최대값을 현재값으로 변경하고
                high = answer[i];
                // 현재의 인덱스를 기억하고
                num = i;
                // 아직까지는 중복최대값이 아니므로 same을 false로 바꿈.
                same = false;
            }
        }

		// 최대값이 2개이상이면 ?, 하나만 존재한다면 해당 인덱스의 알파벳 출력.
        if (same)
        {
            return '?';
        }
        else
        {
            return Max[num];
        }
    }
}

 

반응형

댓글