[알고리즘] 1157 단어 공부

DongGyu Jung·2022년 1월 16일
0
post-thumbnail

게시물을 작성하면서 복습하는 문제를 선정하는 기준은<solved.ac 티어 브론즈 1 (Bronze1) 이상>입니다.

※ 본 사진과 해당 게시글 내용의 문제 모두 백준 : 온라인 저지[Baekjoon_OnlineJudge]사이트에서 발췌해왔습니다.

❓ 문제

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

<입력>
첫째 줄에 알파벳 대소문자로 이루어진 단어가 주어진다. 
(주어지는 단어의 길이는 1,000,000을 넘지 않는다.)

<출력>
첫째 줄에 이 단어에서 가장 많이 사용된 알파벳을 대문자로 출력한다. 
(단, 가장 많이 사용된 알파벳이 여러 개 존재하는 경우에는 ?를 출력한다.)

[Input]		|  [Output]
Mississipi	|  ?
zZa		|  Z
z		|  Z
baaa		|  A

❗ 풀이

My Code

메모리 : 41872 KB
시간 : 180ms
(시간이 좀 길게 걸렸다... 모듈을 사용하면서 무거워진 듯 하다)

from collections import Counter

v = [alp for alp in input().upper()] # 대문자 출력 조건이기때문에 먼저 upper 해주기
c = Counter(v) # 리스트에 각 요소별 빈도값을 튜플형 _ [(value, count),...]으로 생성

mst =  c.most_common() # 빈도값 정렬
max = mst[0][1] # 맨앞값이  최빈값 튜플이기때문에 최빈값 index로 최빈값 변수에 대입

# 최빈값이 중복될 때 대비
max_alps = []
for cnt in mst : # 정렬한 순서대로 뒤 count값을 서로 비교하기
    if cnt[1] == max:
        max_alps.append(cnt[0]) 
        # 만약 Counter 객체의 맨 앞값과 동일한 값이 뒤에 있으면 똑같이 append -> ?출력할때 사용


if len(max_alps) == 1 : # 최빈값을 가진 값이 하나일 땐 해당 알파벳 출력
    print(max_alps[0]) # 빈도수 말고 알파벳 출력
else : # 두 개 이상의 알파벳의 출현횟수가 동일할 경우 : len(max_alps) > 1 경우
    print('?')

백준에서는
프로그래머스와는 달리 예제가 얼마나 있는지,
코드의 효율성이 얼만큼인지 확인할 수 가 없다...
개인적인 예상으로는 collections.Counter를 사용해서 복잡도가 증가한 것 같다.


❣ 다른 풀이

(1)

import sys
word = sys.stdin.readline()
    ALPHABET = list(range(0x41, 0x5A+1))  # 아스키코드 숫자 범위 (대문자)
    alphabet = list(range(0x61, 0x7A+1))  # 아스키코드 숫자 범위 (소문자)
    cnt = [0] * 26

    for i in range(0, 26) :
        cnt[i] += word.count(chr(ALPHABET[i]))
        cnt[i] += word.count(chr(alphabet[i]))

    if cnt.count(max(cnt)) > 1:
        print("?")
    else:
        # 복잡한 출력식이라 가독성은 별로라고 느껴짐
        print(chr(ALPHABET[cnt.index(max(cnt))]))

이 풀이는 컴퓨터와 직접적인 소통을하는 듯한 방법이다..ㅋㅋㅋㅋ
아스키 코드 16진수 숫자범위를 통해서
.count()함수의 인수로
chr([아스키코드 i번째 16진수 값]) 를 투입해서
" 16진수값에 해당하는 알파벳으로 변환 후, 해당 알파벳을 count "하는 방법을 사용했다.

이 count한 값은
미리 만들어둔 cnt라는 26개의 0으로 이루어진 빈 리스트에 더해주는데
대문자의 경우와 소문자의 경우를 한번에 합해서
" 해당 알파벳의 빈도수 "를 빠짐없이 측정한다.

이후
조건문에서 다시 count()함수를 사용해서
이번엔 빈도수를 저장한 cnt 리스트에서
최빈값이 몇 개인가를 세보았고

최빈값이 중복된다면 (count(max(cnt)) > 1) ?를 출력하게하고
그 외에는
cnt리스트의 최댓값(최빈값)을 가진 알파벳 위치 (cnt.index(max(cnt)))를
다시 " 대문자 알파벳 16진수 아스키코드 리스트에서의 인덱스 "로 사용해서
chr()함수를 통해 대문자로 변환하여 출력하게끔 만들었다.


(2)

S = input().upper() # 입력받고, 처음부터 모두 대문자로 바꿔줌 (출력할 때도 편하고, 셀 때도 편하게)
sarray = list(set(S)) #  앞에서부터 나온 알파벳의 종류만 저장 (중복 X)
array = [] # 빈도수를 저장할 리스트

for s in sarray:
    array.append(S.count(s)) # 문자열 S에서 sarray를 구성하는 각각의 알파벳 빈도수를 array에 추가 
mc = max(array) # 배열 array에서 max값 : 최빈값 
if array.count(mc) != 1: # 최빈값을 가진 값의 개수가 1이 아니면 동일한 빈도수 값이 더 있다는 것  
    print("?") 
else:
    mi = array.index(mc) # array배열에서 최빈값이 있는 index찾아서 mi에 저장 
    # array에 append되는 순서도 그대로 sarray를 따르기 때문에 index를 그대로 사용할 수 있음.
    print(sarray[mi]) # array 인덱스와 sarray가 같기 때문에 max값 인덱스번째 값을 찾음 

.upper()를 쓰는 것은 필자의 경우와 같은데
이 풀이에서는
Set 형을 사용해서 중복값을 제거한 것이 인상적이다.


(3)

word = input().upper()

#각 알파벳의 갯수를 세기 위한 배열
count_alph = [0] * 26

for i in range(len(word)):
    for k in range(26):
        #아스키코드로 65->A 인점을 활용해서
        # 정직했던 (1) 풀이를 조금 유동적으로 변형한 것
        if word[i] == chr(65+k):
            count_alph[k] += 1

if count_alph.count(max(count_alph)) > 1:
    print('?')
else:
    for i in range(26):
        if count_alph[i] == max(count_alph):
            print(chr(65+i))

이 풀이도 (1)풀이와 같이 아스키 코드chr()함수를 활용했는데

10진수로 65~90A~Z라는 것을 활용해
조금 더 간결하게 바꾼 경우이다.

뭐가 맞다할 수 없지만
16진수 활용법과 아스키코드를 대부분 외운 경우가 아니라면
(3)풀이처럼 10진수를 활용하는 것도 좋은 방법이다.

0개의 댓글