[프로그래머스] Lv.2 [1차] 뉴스 클러스터링 (Python)

seulzzang·2022년 11월 7일
0

코딩테스트 연습

목록 보기
38/44
post-thumbnail

📍문제

[프로그래머스] Lv.2 [1차] 뉴스 클러스터링

📍풀이

  • ex = "~!@#$%^&*()_+=- /?|/\"\'\\1234567890,.<>;:"을 선언해줘서 알파벳이 아닌 문자열들을 골라내기로 하였다.
  • 이후 교집합과 합집합을 구하는 코드를 짜고, 자카드 유사도를 구하는 공식으로 return해주면 끝. 합집합이 비어있다면 return 65536을 해주고, 아니라면 교집합의 크기/합집합의 크기 * 65536으로 답을 구해준다.
  • 다중집합을 직접적으로 건들이지 않기 위해 copy()를 사용해줬다.

💻나의 코드

def solution(str1, str2):
    # 자카드 유사도: 교집합크기/합집합크기
    # 대소문자는 무시 -> 다 소문자로 만들기
    str1 = str1.lower()
    str2 = str2.lower() 

    ex = "~!@#$%^&*()_+=- /?|/\"\'\\1234567890,.<>;:"
    g1, g2 = [], []
    sum_group, inter_group = [], []
    
    # 입력으로 들어온 문자열은 두 글자씩 끊어서 다중집합의 원소로 만든다.
    for i in range(1, len(str1)):
        if str1[i-1] in ex or str1[i] in ex:
            pass
        else:
            g1.append(str1[i-1]+str1[i])

    for i in range(1, len(str2)):
        if str2[i-1] in ex or str2[i] in ex:
            pass
        else:
            g2.append(str2[i-1]+str2[i])

    g1_copy = g1.copy()
    g2_copy = g2.copy()
    # 교집합 구하기
    for g in g1:
        if g in g2_copy:
            inter_group.append(g)
            g1_copy.remove(g)
            g2_copy.remove(g)
    # 합집합 구하기
    sum_group = inter_group + g1_copy + g2_copy
    
    # 이를 다루기 쉽도록 65536을 곱한 후에 소수점 아래를 버리고 정수부만 출력한다.
    if len(sum_group) == 0:
        return 65536
    else:
        return int(len(inter_group)/len(sum_group) * 65536)

살짝 무식하게 풀었는데.. 키보드에 존재하는 특수문자들을 ex라는 문자열로 선언해주고 str1str2가 특수문자열 ex에 있으면 g1, g2에 넣어주지 않는다. 이렇게 하면 이때 영문자로 된 글자 쌍만 유효하고, 기타 공백이나 숫자, 특수 문자가 들어있는 경우는 그 글자 쌍을 버린다.를 만족하는 문자열들을 집합에 담을 수 있다.

교집합을 구할 때 g1_copyg2_copy에서 교집합 안에 들어간 원소를 remove해주지 않으면 제대로된 합집합을 구할 수 없다!!
따라서 이를 제거해 준 다음에 합집합에 더해주면 된다.

알파벳만 다루는 방법

나는 다소 무식하게 풀었지만, 다른 사람들 풀이를 보면 정규표현식, isalpha()를 이용하여 알파벳만 추출하는 방법들이 존재해서 이를 정리하고자 한다.

1. 정규표현식

import re

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])]

나의 구구절절한 코드를 두줄로 축약 가능

2. isalpha()

list1 = [str1[n:n+2].lower() for n in range(len(str1)-1) if str1[n:n+2].isalpha()]
list2 = [str2[n:n+2].lower() for n in range(len(str2)-1) if str2[n:n+2].isalpha()]

두개씩 끊은 문자열이 알파벳이라면, 소문자로 바꾸고 리스트에 담아준다!
이 역시 나의 구구절절한 코드보다 훨씬 간단한 부분이다.

📍다른사람 풀이

💻Counter 사용

from collections import Counter
def solution(str1, str2):
    # make sets
    s1 = [str1[i:i+2].lower() for i in range(len(str1)-1) if str1[i:i+2].isalpha()]
    s2 = [str2[i:i+2].lower() for i in range(len(str2)-1) if str2[i:i+2].isalpha()]
    if not s1 and not s2:
        return 65536
    c1 = Counter(s1)
    c2 = Counter(s2)
    answer = int(float(sum((c1&c2).values()))/float(sum((c1|c2).values())) * 65536)
    return answer

wow
교집합과 합집합 역시 간결하게 나타내 준 코드이다!!
교집합은 & , 합집합은 | 표현으로 set함수와 똑같이 작동한다.
예제 1번을 통해서 설명을 하자면

str1, str2 = 'FRANCE', 'french'
s1 = [str1[i:i+2].lower() for i in range(len(str1)-1) if str1[i:i+2].isalpha()]
s2 = [str2[i:i+2].lower() for i in range(len(str2)-1) if str2[i:i+2].isalpha()]

알파벳만 존재하는 다중집합을 만들어 주고

c1 = Counter(s1)
c2 = Counter(s2)
(Counter({'fr': 1, 'ra': 1, 'an': 1, 'nc': 1, 'ce': 1}),
 Counter({'fr': 1, 're': 1, 'en': 1, 'nc': 1, 'ch': 1}))

위와 같이 Counter객체를 생성한다.

c1&c2
Counter({'fr': 1, 'nc': 1})

이렇게 교집합을 구할 수 있다.

(c1&c2).values()
dict_values([1, 1])

이렇게 원소들의 값만 구하여

sum((c1&c2).values())
2

합을 구하면 집합의 크기가 된다.
우리에게 필요한 값은 어느 원소가 교집합, 합집합에 속해있느냐가 아니라 교집합, 합집합의 크기이기 때문에 이렇게 구해주면 아주 간단하게 코드를 짤 수 있다!

참조

파이썬 집합자료형 set 연산 : 합집합, 교집합, 차집합
collections 모듈의 Counter 클래스


정규표현식을 배웠고, isalpha()를 사용해 다양한 문제를 풀었음에도.. 또 Counter클래스를 사용해봤음에도!!! 무식하게 문제를 풀었다..😂 배운 것을 잘 써먹는 것 또한 매우매우 어려운 부분인 것 같다😢

profile
중요한 것은 꺾이지 않는 마음

0개의 댓글