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

eora21·2022년 9월 6일
0

프로그래머스

목록 보기
17/38

문제 링크

문제 간단 해석

주어진 문자열을 2개씩 끊되, 알파벳으로만 이루어진 집합을 만들어 나머지 집합과 비교하는 문제.

Java

풀이 코드

import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

class Solution {
    int total = 0;
    
    public Map<String, Integer> createMap(String s) {
        Map<String, Integer> map = new HashMap<>();
        String now;
        
        for (int i = 2; i < s.length() + 1; i++) {
            now = s.substring(i - 2, i);
            
            if (now.replaceAll("[^a-z]","").length() < 2)
                continue;
            
            total++;
            
            try {
                map.put(now, map.get(now) + 1);
            } catch (Exception e) {
                map.put(now, 1);
            }
        }
        
        return map;
    }
    
    public int solution(String str1, String str2) {
        Map<String, Integer> map1 = createMap(str1.toLowerCase());
        Map<String, Integer> map2 = createMap(str2.toLowerCase());
        
        if (total == 0)
            return 65536;
        
        Set<String> set1 = new HashSet<>(map1.keySet());
        Set<String> set2 = new HashSet<>(map2.keySet());
        set1.retainAll(set2);
        
        int cnt = 0;
        
        for (String s: set1)
            cnt += Math.min(map1.get(s), map2.get(s));

        return (int)((double)cnt / (total - cnt) * 65536);
    }
}

해석

Map<String, Integer> map1 = createMap(str1.toLowerCase());
Map<String, Integer> map2 = createMap(str2.toLowerCase());

Map 2개를 생성하는데, 소문자로 구성되게끔 한 문자열을 넘긴다.

public Map<String, Integer> createMap(String s) {
    Map<String, Integer> map = new HashMap<>();
    String now;

    for (int i = 2; i < s.length() + 1; i++) {
        now = s.substring(i - 2, i);

        if (now.replaceAll("[^a-z]","").length() < 2)
            continue;

        total++;

        try {
            map.put(now, map.get(now) + 1);
        } catch (Exception e) {
            map.put(now, 1);
        }
    }

    return map;
}

HashMap을 우선 생성하고, 받은 문자열을 2글자씩 끊는다.
이 때, 소문자 알파벳을 제외한 나머지 글자들은 없앤다. 만약 글자가 한글자라도 지워진다면 for문을 재가동한다.

소문자 알파벳으로만 이뤄진 문자열을 map의 key로 넣어주며 전체 문자열의 갯수를 1 증가시킨다.
해당 key가 이미 있었을 시 value 1을 증가시켜주며, 없을 시 해당 문자열과 1을 map에 넣어준다.

그 후 map을 반환.

if (total == 0)
    return 65536;

만약 아무런 문자열이 생성되지 않아 양쪽 다 빈 집합이라면 65536을 반환한다.

Set<String> set1 = new HashSet<>(map1.keySet());
Set<String> set2 = new HashSet<>(map2.keySet());
set1.retainAll(set2);

map의 key들을 set으로 만든 후 공집합화한다.

int cnt = 0;

for (String s: set1)
    cnt += Math.min(map1.get(s), map2.get(s));

공집합의 문자열을 하나씩 꺼내 양쪽 집합을 확인한다. 보다 작은 수를 기준으로 공집합이 활성화되니 작은 수를 가져오게끔 한다.

return (int)((double)cnt / (total - cnt) * 65536);

cnt를 double로 만들어 소수점 결과가 나오게 한다.
total에서 중복된 cnt만큼을 빼 주고 나눈다.
65536을 곱한다.
int로 소수점을 버린 후 반환.

Python

비효율적으로 풀었다. 다른 사람의 풀이를 참고할 것.

풀이 코드

def solution(str1, str2):
    str_group = [[], []]
    str1 = str1.upper()
    str2 = str2.upper()
    s = [str1, str2]
    
    for d in range(2):
        for i in range(1, len(s[d])):
            two_char = s[d][i - 1: i + 1]
            if two_char.isalpha():
                str_group[d].append(two_char)
    
    both = []
    total = str_group[0][:]
    
    for st in str_group[1]:
        if st in str_group[0]:
            both.append(st)
            str_group[0].remove(st)
        else:
            total.append(st)
            
    return int(65536 * (len(both) / len(total) if len(total) != 0 else 1))

해석

str_group = [[], []]
str1 = str1.upper()
str2 = str2.upper()
s = [str1, str2]

2글자 문자열만을 담을 str_group과 기존 문자열을 대문자화한 str1, str2를 선언하여 s에 담았다.

for d in range(2):
    for i in range(1, len(s[d])):
        two_char = s[d][i - 1: i + 1]
        if two_char.isalpha():
            str_group[d].append(two_char)

양쪽의 문자열을 다 확인, 2글자씩 자르고 알파벳으로만 구성된건지 확인한다.
그 후 str_group에 담는다.

both = []
total = str_group[0][:]

양 쪽 모두 존재하는 문자열을 담을 both와 첫번째 문자열에서 생성된 2글자 리스트를 복사한 total을 준비시킨다.

for st in str_group[1]:
    if st in str_group[0]:
        both.append(st)
        str_group[0].remove(st)
    else:
        total.append(st)

두번째 문자열에서 생성된 2글자 문자열을 확인한다.
만약 첫번째 문자열 집합에 해당 2글자 문자열이 있다면, both에 추가하고 첫번째 문자열 집합에서 제거한다.
없다면, 전체 문자열에 추가한다.

return int(65536 * (len(both) / len(total) if len(total) != 0 else 1))

전체 문자열에 아무것도 없다면 65536을 반환.
아닐 경우 공집합의 길이 / 전체집합의 길이 * 65536을 반환한다.

여담

파이썬 코드를 보자마자 '아, 과거의 내가 비효율적으로 풀었구나'라는 생각이 확 들었다.
코드 가독성도 그렇고, 시간도 꽤나 오래 걸릴 것이다.
과거에 비해 실력이 늘긴 늘었나 보다.
그래도.. 아직 멀었지.

profile
나누며 타오르는 프로그래머, 타프입니다.

0개의 댓글