프로그래머스_[1차] 뉴스 클러스터링

임정민·2023년 10월 26일
0

알고리즘 문제풀이

목록 보기
120/173
post-thumbnail

프로그래머스 Lv2 문제입니다. 실전에 대비하기 위해 60분 시간제한을 두고 풀었습니다.

문제

https://school.programmers.co.kr/learn/courses/30/lessons/17677

[나의 풀이]

⌛ 시간 초과 (69.2/100점)


import re
import math

def get_words(str_n):

    words = []
    
    check = r'[^a-zA-Z]'
    
    same_cnt = 1
    for i in range(len(str_n)-1):
        
        word = str_n[i]+str_n[i+1]
        
        if re.sub(check,"",word) != word:
            print("word : ",word)
            print("탐지!")
            continue
        
        word = word.upper()
        
        # if word in words:
        #     word += str(same_cnt)
        #     same_cnt += 1
        
        if word in words:
            tmp = str(same_cnt)+word
            same_cnt += 1
            word = tmp
        
        words.append(word)
    
    return words

def get_Jaccard(words1,words2):
    
    inter = list(set(words1) & set(words2))
    union = list(set(words1) | set(words2))
    
    jaccard = len(inter)/len(union)
    return len(inter)/len(union)

def solution(str1, str2):
    
    words1 = get_words(str1)
    print(words1)
    words2 = get_words(str2)
    print(words2)
    
    if words1==[] and words2==[]:
        return 65536
    
    jaccard = get_Jaccard(words1,words2)
    print(jaccard)
    print(math.floor(jaccard*65536))
    
    return math.floor(jaccard*65536)

중복 원소를 허용하는 두 집합의 자카드 유사도를 구하는 문제입니다. 이때 각 집합의 원소는 주어지는 각 문자열을 통해 만들어집니다. 예를 들어, 'FRANCE', 'french' 와 같은 문자열이 입력된다면 이어지는 두 영문자씩 쌍을 지어 한 원소가 됩니다. 이때, 특수문자나 숫자는 해당이 안되기 때문에 정규식으로 처리해주었습니다.🐺🐺🐺

다음으로 각 집합에 동일한 문자열 원소이더라도 중복이 가능하게끔 구현하기 위해 이전에 동일한 원소가 있다면 +n씩 더하는 방식으로 연산하였는데 일부케이스에서 실패하여 오랜고민 끝에 다른 풀이를 참고하여 이유를 알게 되었습니다.

위 풀이로 구현시 'aa+aa+bb+bb','AAAA+BBBB' 와 같이 입력될 때 제가 원하는 결과는


words1= ['aa', 'aa@', 'bb', 'bb@']
words2 = = ['aa', 'aa@', 'aa@@', 'bb', 'bb@', 'bb@@']

이지만 서로 다른 원소이더라도 이전에 동일한 원소가 탐지될 때마다 same_cnt +=1 하기 때문에


words1 = ['aa', 'aa@', 'bb', 'bb@@'] 
words2 =  ['aa', 'aa@', 'aa@@', 'bb', 'bb@@@', 'bb@@@@' ]

으로 저장되는 것이 문제라는 것을 깨닫게 되었습니다.

[다른 사람의 풀이1]


from collections import Counter

def solution(str1, str2):
    str1_low = str1.lower()
    str2_low = str2.lower()
    
    str1_lst = []
    str2_lst = []
    
    for i in range(len(str1_low)-1):
        if str1_low[i].isalpha() and str1_low[i+1].isalpha():
            str1_lst.append(str1_low[i] + str1_low[i+1])
    for j in range(len(str2_low)-1):
        if str2_low[j].isalpha() and str2_low[j+1].isalpha():
            str2_lst.append(str2_low[j] + str2_low[j+1])
            
    Counter1 = Counter(str1_lst)
    Counter2 = Counter(str2_lst)
    
    inter = list((Counter1 & Counter2).elements())
    union = list((Counter1 | Counter2).elements())
    
    if len(union) == 0 and len(inter) == 0:
        return 65536
    else:
        return int(len(inter) / len(union) * 65536)
    
# solution('FRANCE','french')
solution('aa1+aa2','AAAA12')

영문자만을 추출하기 위해 isalpha()함수를 사용하고 Counter()를 활용하여 교/합집합을 구현한 풀이입니다.

두 Counter() 객체에 집합의 영문자 요소를 저장한 뒤Counter()간의 &연산을 통해 교집합을 구하고 |연산을 통해 합집합을 구하는 방식입니다. 마지막 교집합/합집합.elements()을 리스트로 형변환하여 자카드 유사도를 구할 수 있었습니다.🧸🧸🧸

[다른 사람의 풀이2]


import re
import math

def solution(str1, str2):

    str1 = [str1[i:i+2].lower() for i in range(0, len(str1)-1) if not re.findall('[^a-zA-Z]+', str1[i:i+2])]
    str2 = [str2[i:i+2].lower() for i in range(0, len(str2)-1) if not re.findall('[^a-zA-Z]+', str2[i:i+2])]

    gyo = set(str1) & set(str2)
    hap = set(str1) | set(str2)

    if len(hap) == 0:
        return 65536

    gyo_sum = sum([min(str1.count(gg), str2.count(gg)) for gg in gyo]) # 교
    hap_sum = sum([max(str1.count(hh), str2.count(hh)) for hh in hap]) # 합

    return math.floor((gyo_sum/hap_sum)*65536)

가장 파이써닉하고 간결한 풀이였습니다.

먼저 2글자의 영문자로만 이루어진 리스트 str1, str2를 각각 구합니다. 그 후 중복없는 교/합집합을 구합니다. 마지막으로 중복있는 교집합을 구하기 위해 중복없는 교집합의 요소가 있는 str1,str2별 갯수의 최솟값들을 더하고 합집합을 구하기 위해 str1,str2별 갯수의 최대값들을 더하는 풀이입니다.🐰🐰🐰

중복없는 교/합집합을 활용하여 중복있는 교/합집합을 구하는 아이디어 자체가 좋았던 풀이입니다.

감사합니다.

profile
https://github.com/min731

0개의 댓글