불량 사용자

dasd412·2022년 2월 7일
0

코딩 테스트

목록 보기
10/71

문제 설명

개발팀 내에서 이벤트 개발을 담당하고 있는 "무지"는 최근 진행된 카카오이모티콘 이벤트에 비정상적인 방법으로 당첨을 시도한 응모자들을 발견하였습니다. 이런 응모자들을 따로 모아 불량 사용자라는 이름으로 목록을 만들어서 당첨 처리 시 제외하도록 이벤트 당첨자 담당자인 "프로도" 에게 전달하려고 합니다. 이 때 개인정보 보호을 위해 사용자 아이디 중 일부 문자를 '' 문자로 가려서 전달했습니다. 가리고자 하는 문자 하나에 '' 문자 하나를 사용하였고 아이디 당 최소 하나 이상의 '*' 문자를 사용하였습니다."무지"와 "프로도"는 불량 사용자 목록에 매핑된 응모자 아이디를 제재 아이디 라고 부르기로 하였습니다.

예를 들어, 이벤트에 응모한 전체 사용자 아이디 목록이 다음과 같다면







다음과 같이 불량 사용자 아이디 목록이 전달된 경우,




불량 사용자에 매핑되어 당첨에서 제외되어야 야 할 제재 아이디 목록은 다음과 같이 두 가지 경우가 있을 수 있습니다.







이벤트 응모자 아이디 목록이 담긴 배열 user_id와 불량 사용자 아이디 목록이 담긴 배열 banned_id가 매개변수로 주어질 때, 당첨에서 제외되어야 할 제재 아이디 목록은 몇가지 경우의 수가 가능한 지 return 하도록 solution 함수를 완성해주세요.

[제한사항]

  • user_id 배열의 크기는 1 이상 8 이하입니다.
  • user_id 배열 각 원소들의 값은 길이가 1 이상 8 이하인 문자열입니다.
    • 응모한 사용자 아이디들은 서로 중복되지 않습니다.
    • 응모한 사용자 아이디는 알파벳 소문자와 숫자로만으로 구성되어 있습니다.
  • banned_id 배열의 크기는 1 이상 user_id 배열의 크기 이하입니다.
  • banned_id 배열 각 원소들의 값은 길이가 1 이상 8 이하인 문자열입니다.
    • 불량 사용자 아이디는 알파벳 소문자와 숫자, 가리기 위한 문자 '*' 로만 이루어져 있습니다.
    • 불량 사용자 아이디는 '*' 문자를 하나 이상 포함하고 있습니다.
    • 불량 사용자 아이디 하나는 응모자 아이디 중 하나에 해당하고 같은 응모자 아이디가 중복해서 제재 아이디 목록에 들어가는 경우는 없습니다.
  • 제재 아이디 목록들을 구했을 때 아이디들이 나열된 순서와 관계없이 아이디 목록의 내용이 동일하다면 같은 것으로 처리하여 하나로 세면 됩니다.

전체 코드

from itertools import permutations

def is_correct(user, ban):#user_id와 banned_id 대조
    if len(user)!=len(ban):#길이가 다르면 무조건 다른 것.
        return False
    else:#길이가 같으면, 대조할 필요가 있다.
        count=0#같은 개수 확인하는 변수
        for i in range(len(user)):
            #i번째가 아스타리스크거나 유저 i번째 글자와 같다면
            if ban[i]=='*' or user[i]==ban[i]:
                count+=1
            else:#아니라면 더 볼 필요 없이 종료
                return False
        
        return True
                
        

def solution(user_id, banned_id):
    answer = 0
    
    answer_set=set()#후보담는 set
    
    #len(user_id) P len(banned_id)로 순열 리스트 만들기
    result=list(permutations(user_id,len(banned_id)))
    
    #순열 리스트 순회
    for i in range(len(result)):
        
        #밴 후보들  담기
        candidate=[]
        
        #뽑은 순열에 대해 검사
        for j in range(len(result[0])):
            #밴 대상이 될 수 있다면, 밴 후보로 담는다.
            if is_correct(result[i][j],banned_id[j]):
                candidate.append(result[i][j])
                
        #밴 후보 set이 밴 대상 리스트 길이와 같으면, 정답 후보를 하나 찾은 것이다.
        if len(candidate)==len(banned_id):
            tup=tuple(sorted(candidate))
            answer_set.add(tup)
    
    answer=len(answer_set)
    
    return answer

해설

1.user_id를 banned_id 개수만큼 뽑고 순서대로 나열해보자.

순열을 활용할 수 있는 이유는 user_id와 banned_id 모두 길이가 충분히 작기 떄문이다. (최대 8)

배열에 대한 순열은 배열 길이가 길면 길수록 시간 복잡도가 기하급수적으로 증가함을 기억하자.

from itertools import permutations

#len(user_id) P len(banned_id)로 순열 리스트 만들기
result=list(permutations(user_id,len(banned_id)))

예시에 대해 이를 적용해보면 다음과 같다.

예시 ["frodo", "fradi", "crodo", "abc123", "frodoc"], ["fr*d*", "abc1**"]

결과 [('frodo', 'fradi'), ('frodo', 'crodo'), ('frodo', 'abc123'), ('frodo', 'frodoc'), ('fradi', 'frodo'), ('fradi', 'crodo'), ('fradi', 'abc123'), ('fradi', 'frodoc'), ('crodo', 'frodo'), ('crodo', 'fradi'), ('crodo', 'abc123'), ('crodo', 'frodoc'), ('abc123', 'frodo'), ('abc123', 'fradi'), ('abc123', 'crodo'), ('abc123', 'frodoc'), ('frodoc', 'frodo'), ('frodoc', 'fradi'), ('frodoc', 'crodo'), ('frodoc', 'abc123')]

2.순열 리스트를 순회하며 밴 후보들을 찾아내자.

def is_correct(user, ban):#user_id와 banned_id 대조
    if len(user)!=len(ban):#길이가 다르면 무조건 다른 것.
        return False
    else:#길이가 같으면, 대조할 필요가 있다.
        count=0#같은 개수 확인하는 변수
        for i in range(len(user)):
            #i번째가 아스타리스크거나 유저 i번째 글자와 같다면
            if ban[i]=='*' or user[i]==ban[i]:
                count+=1
            else:#아니라면 더 볼 필요 없이 종료
                return False
        
        return True

#......

    #순열 리스트 순회
    for i in range(len(result)):
        
        #밴 후보들  담기
        candidate=[]
        
        #뽑은 순열에 대해 검사
        for j in range(len(result[0])):
            #밴 대상이 될 수 있다면, 밴 후보로 담는다.
            if is_correct(result[i][j],banned_id[j]):
                candidate.append(result[i][j])
                
        #밴 후보 set이 밴 대상 리스트 길이와 같으면, 정답 후보를 하나 찾은 것이다.
        if len(candidate)==len(banned_id):
            tup=tuple(sorted(candidate))
            answer_set.add(tup)

순열 리스트 result에는 각 인덱스마다 순열이 존재한다.

i번째 순열이 ('frodo', 'abc123')라고 할 때, 이를 banned_id ["frd", "abc1**"]와 대조해본다.

대조하는 함수는 is_correct()로 구현하였다.

밴 아이디 길이와 같고, 같은 글자의 수도 동일하다면 밴 목록에 들어갈 수 있는 ID임을 뜻한다.

대조를 하면서 밴 목록에 들어갈 수 있는 id는 candidate라는 리스트에 밴 후보로 담는다.

그리고 candidate의 길이가 banned_id의 길이가 같다면 정답 후보를 하나 찾은 것이라 볼 수 있다.

그런데 문제의 조건 중,

제재 아이디 목록들을 구했을 때 아이디들이 나열된 순서와 관계없이 아이디 목록의 내용이 동일하다면 같은 것으로 처리하여 하나로 세면 됩니다.

라는 조건이 있다.

예를 들어 banned_id=["f**","f**"]이고 candidate=('frodo', 'fredi')과 candidate =('fredi',"frodo")라면, 두 candidate는 같은 것으로 취급되야 한다.

따라서 이를 sorted()로 정렬해준 후, 튜플로 만들어준다.
그리고 이 튜플을 set에 담아주면 set이 중복되는 것을 걸러준다.
(튜플로 만들어준 이유는 set에 담으려면 담을 원소가 해시 가능한 타입, 즉 불변타입이어야 하기 때문이다.)

profile
아키텍쳐 설계와 테스트 코드에 관심이 많음.

0개의 댓글